Angular vs. React is a popular debate among front-end JavaScript developers and, more often than not, the discussion ends up being biased towards one technology or the other. Developed by Google and Facebook respectively, Angular and React are the two popular technologies used to build interactive single-page applications.
A comprehensive comparison between Angular and React is imminent because there are certain places in which they significantly overlap in terms of what they offer, i.e. building the front-end view of your application and other places where their functionality remains incomplete unless helped by a third-party library. Adopting one technology over the other is a question of whether Angular or React better solves your problem and a bit of intuition. In this tutorial, we’ll compare and contrast seven key different features of Angular and React.
I am an ardent proponent of the code-first approach (code speaks louder than words, they say). Keeping this in mind, I’ve added code samples of Angular and React wherever possible so that you can build on your intuition and decide which works for you and which doesn’t. Let’s get started.
Framework vs. Library
Angular is a framework, while React is a library.
So what does this mean? React on its own won’t let you create a web application because it is designed to create views (hence the ‘V’ in MVC). What React can do is build component-based views for which data can be passed down to child views. To fill this void, Facebook has developed Flux, which is an architectural pattern that complements React. Flux architecture, when coupled with React, provides the following scenario:
- The user clicks on a React element.
- An action is fired. This action is dispatched to a Store via a Dispatcher library.
- The Store keeps track of the application’s state and data retrieval methods. Any update to the state is reflected in the views, and this helps to keep the views consistent with the state of the application.
Doesn’t make sense? This figure should sort it out for you.
Angular is a framework for building client applications.
AngularJS was firmly built on top of the MVC pattern, which separated the application into three different layers. The combination of model, view, and controller plus the added complexity involved in mastering directives, factories, services and other components to create a single-page application forced the developers at Google to shift towards a component-based architecture.
But when your application starts to grow, it’s important to have a solid structure that keeps the business logic of your application away from the components. Being a framework, Angular allows you to enforce structural organization by moving the business rules into a domain model (using a combination of model classes and services) and injecting the model into your components via dependency injection.
Here is a sample of code that illustrates how the business logic is encapsulated inside a User model and a User service, and away from our component.
/* Path: /app/models/User.ts */ export class User { id: number; username: string; password: string; firstName: string; lastName: string; }
/* /app/services/user.service.ts */ import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import { User } from '../models/User'; @Injectable() export class UserService { constructor(private http: Http) { } getAll() { // API to return all users } create(user: User) { //API call to create user } update(user: User) { //API call to update user } delete(id: number) { //API call to delete user } }
/* Path: /app/page/page.component.ts */ import { Component } from '@angular/core'; import { User } from '../models/User'; import { UserService } from '../services/user.service'; @Component({ templateUrl: 'page.component.html' }) export class PageComponent { currentUser: User; users: User[] = []; constructor(private userService: UserService) { //Dependency is Injected inside the constructor's arguments deleteUser(id: number) { this.userService.delete(id).subscribe(() => { #Do Something}); } private loadAllUsers() { this.userService.getAll().subscribe(users => { #Do something else }); } }
All users:
- {{user.username}} ({{user.firstName}} {{user.lastName}}) - Delete
Component-Based Approach
Components are the most basic building block of an UI in an Angular application. An Angular application is a tree of Angular components.
What are components? In Angular, components are TypeScript classes that have a @Component
decorator marked over them. Moreover, inside these decorators, we can define what Angular calls the meta-data, which includes the template, styles, selectors and so forth.
Component hierarchy in Angular is designed in such a way that you can associate structure and functionality under a single entity. Here is a high-level architectural overview of components and how this links to everything else in Angular.
Data sharing among components is possible by nesting components, as exemplified below.
/* UserParentComponent.ts */ import { Component } from '@angular/core'; // Theselector is nested inside . Each user is passed down as a property. @Component({ selector: 'user-parent', template: ` There are {{users.length}} registered users {{status}} now
` }) export class UserParentComponent { users: { id: number, name: string }[] = [ { "id": 0, "name": "Chris" }, { "id": 1, "name": "Dwayne" }, { "id": 2, "name": "Eve" } ]; status: string = "online"; }
/* UserChildComponent.ts */ import { Component, Input } from '@angular/core'; // Input properties are adorned with @decorators // user & status are input properties @Component({ selector: 'user-child', template: `{{user.name}}
id : {{user.id}}
Status: {{status}}
` }) export class UserChildComponent { @Input() user: { id: number, name: string }; @Input() status: string; }
The concept of components is deeply rooted in React, just as it is in Angular. Facebook calls React a component-based library that lets you build interactive user interfaces. However, unlike Angular, React components are just JavaScript functions with an arbitrary number of inputs and an output. The code below shows a component defined using a JavaScript function and using an ES6 class.
// Writing components using JavaScript functions function Welcome(props) { returnHello, {props.name}
; } // Writing components using ES6 Class class Welcome extends React.Component { render() { returnHello, {this.props.name}
; } }
We will be adhering to the ES6 standards for composing components henceforth because that is what Facebook recommends. Each React component accepts an arbitrary number of inputs, which is stored inside an object named props
.
It also has a render
method, and as the name suggests, this method determines what will be rendered when the component is invoked. Each component maintains an internal state (via this.state
), and every time the state changes, the render function of that component is invoked again.
Language Features: TypeScript vs. ES6
Angular applications are written in TypeScript, which is a superset of ECMA2015 and uses a transpiler to compile your strongly typed .ts file to a plain .js file. TypeScript offers language extensions that are designed to make writing in JavaScript easier, and it associates type information with JavaScript entities to enforce type checking and enhance the development workflow.
Some of the key features of TypeScript include optional static typing and support for interfaces, classes, and decorators. (Decorators are functions that are prefixed with ‘@’ and immediately followed by a class, parameter, or a property.)
Let’s dive into React, shall we? One of the important language features in React is evident in this code sample.
function Tweet(props) { return(); }This is a tweet.
Isn’t this great? React lets you embed XML/HTML tags into your JavaScript file, and this is done through JSX, which offers syntax extension capability to JavaScript. We have to use a compiler like Babel, which compiles our JSX code into the JavaScript that browsers can understand. The above code compiles down to this:
"use strict"; function Tweet(props) { return React.createElement( "div", { className: "tweet" }, React.createElement("img", { src: "http://twitter.com/some-avatar.png", className: "tweet__avatar" }), React.createElement( "div", { className: "tweet__body" }, React.createElement( "p", null, "This is a tweet." ) ) ); }
Although using JSX is recommended, you can stick with React.createElement()
if you are against the idea of embedding HTML tags into JavaScript.
Furthermore, you can use either ES6 standards or the traditional form of JavaScript while working with React. Although ES6 is relatively new, it adds a lot of features such as classes, arrow functions, template literals, destructing, and the use of let and const. The only downside I can think of is that it adds a little bit of boilerplate code such as the need to call super()
every time you call a constructor()
, and that it doesn’t automatically bind event-handling methods with this
.
class User extends React.Component { constructor(props) { //bloat code alert super(props); this.handleSubmit = this.handleSubmit.bind(this); this.handleInputChange = this.handleInputChange.bind(this); } handleSubmit(user) { //Method to handle submit } handleInputChange(user) { //Method to handle input } render() { return (); } }
Type Checking in Angular vs. PropTypes in React
Static type checking is performed at compile time. The compiler warns you about potential type mismatches and detects certain errors that would otherwise go unnoticed. Additionally, defining a contract on a variable, a property, or the parameters of a function can result in more readable and maintainable code.
Variable and function declarations are made more expressive by declaring their data types. You can read more about the different primitive data types in the TypeScript documentation.
let isLoggedIn: boolean = false; let id: number = 10; let name: string = "Davis"; let list: number[] = [1, 2, 3]; enum Color {Red, Green, Blue}; let c: Color = Color.Red; let bucket: any = 4; bucket = "I can be a string"; bucket = false; // or a boolean
Defining the signature of an API using an interface makes the code less ambiguous and easier to comprehend. The interface serves as a quick start guide that helps you get started with code immediately and saves time otherwise spent on reading the documentation or the actual implementation of the library.
interface ButtonSettings { text: string; size?: { width: number; height: number; }; color?: string; } function createButton(settings: ButtonSettings) { ... } createButton({ text: 'Submit' }); // OK createButton({ text: 'Submit', size: { width: 70, height: 30 }}); // OK createButton({ text: 'Submit', color: 43); // Not OK: 43 isn't a string createButton({ text: 'Submit', size: { width: 70 }); // Not OK: size needs a height as well createButton({ color: 'Blue'}); // Not OK: 'text' member is required
The type
keyword in TypeScript can be used to create an alias for a type. You can then create new types which are a union or intersection of these primitive types.
//Union Types type Age = number | string; function getAge (age: Age): string { return `You are ${age}!`; } let ofSusan: Age =21; let ofTyler: Age = 'thirty one'; getAge(ofSusan); // You are 21! getAge(ofTyler); // You are thirty one! //Intersection Types interface Name{ name(firstName: string, lastName: string): string; } interface Age { age(current: number): number; } // assign intersection definition to alias User type User = Name & Age; function createUser (testUser: User) { testUser.name("David","John"); testUser.age(99); testUser.address(); //error
React has limited support for type checking because the underlying ES6 doesn’t support it. Nevertheless, you can implement type checking using the prop-types
library developed by the React team. Type checking the props
of a component to check whether it is a string can be done as shown below.
import PropTypes from 'prop-types'; //importing prop-types library class Greeting extends React.Component { render() { return (Hello, {this.props.name}
My age is, {this.props.age} ); } } Greeting.propTypes = { name: PropTypes.string; age: PropTypes.number; };
But prop-types
are not limited to strings, numbers, and boolean. You can do a lot more, as described in the prop-types library documentation. However, if you take static type checking seriously, you should use something like Flow, which is a static type-checker library for JavaScript.
Scaffolding: Angular CLI vs. create-react-app
Starting a project from the ground up might seem fun initially. However, the process of setting up the directory structure, writing boilerplate code for components, and getting the application bootstrapped is a futile and unproductive exercise. Your strategy should be to get on with it as quickly as possible and focus on the actual development of the application. Thanks to Google and Facebook, you have tools available to create and scaffold your applications with ease.
Setting up Angular-CLI for angular and create-react-app for React is straightforward using npm.
// Angular CLI $ npm install -g @angular/cli // create-react-app $ npm install -g create-react-app
To create a new Angular application, you should use the following command:
$ ng new PROJECT-NAME $ ng serve
But that’s not it. The ng generate
command lets you generate components, routes, pipes, directives, and services.
$ ng generate component Page installing component create srcapppagepage.component.css create srcapppagepage.component.html create srcapppagepage.component.spec.ts create srcapppagepage.component.ts update srcappapp.module.ts
Angular CLI can do a lot more, like creating a build of your Angular app, commands for running unit tests, and end-to-end testing. You can read more about it on GitHub.
On the other hand, create-react-app
is the officially supported way of creating a React app without any configuration files.
$ npm install -g create-react-app
This should create a functional React app with all the Babel and webpack dependencies taken care of. You can start running the app on your browser using npm start
.
You can find the scripts available for the react app in the package.json file.
"scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } }
Data Binding: Two-Way Binding vs. Unidirectional Binding
Data binding is a feature that enables synchronization of data between the application state (model) and the view. In a one-way data binding routine, any change in the state of the application automagically updates the view. On the contrary, two-way data binding binds together properties and events under a single entity, i.e. any modification of the model updates the view and vice versa.
In React, the properties are passed down from parent to child components, which is known as the unidirectional or top-down data flow. The state of a component is encapsulated and is not accessible to other components unless it is passed down to a child component as a prop, i.e. the state of a component becomes the prop of the child component.
class UserChild extends React.Component { render() { let userData = this.props.users.map( (user) => { return ({user.id} : {user.name}
); }); return (); } } class UserParent extends React.Component { constructor() { super(); //State gets defined here this.state = { status: "Online" } } render() { return (Hello. The server is {this.props.status}
{userData}); } } var USERS = [ { "id": 0, "name": "Chris" }, { "id": 1, "name": "Dwayne" }, { "id": 2, "name": "Eve" } ]; ReactDOM.render(, document.getElementById('container') );
But what if you need to propagate the data up through the component tree? This is done through child events and parent callbacks. The React documentation includes a good example that deals with such a scenario.
Data binding techniques available in Angular are among a few features that make it interesting. Angular has out-of-the-box support for interpolation, one-way binding, two-way binding, and event binding.
Interpolation is the simplest way to bind your component property inside the text between your HTML tags and attribute assignments.
Welcome back {{currentUser.name}}!
Property binding is similar to interpolation in the sense that you can bind the properties of your view elements to component properties. Property binding favors component communication and is identical to how props are passed down in React.
Event bindings allow data flow in the opposite direction, i.e. from an element to a component. Here, click
is a target event, and on the right, we have the onSave()
method that gets invoked when the event occurs.
But the most important feature is the two-way binding using the [(ngModel)]
. This merges the property binding and event binding under one directive and is particularly useful with forms and input fields.
Server-Side Rendering
Server-side rendering is a traditional rendering technique. Here, the server returns the whole HTML file upon request, and the browser is left with the simple job of displaying it to the user. Client-side rendering, on the other hand, returns a bare-bones HTML document, the stylesheet, and a JavaScript file.
The JavaScript makes subsequent requests to render the rest of the website using a browser. React, Angular and all other modern JavaScript front-end libraries are good examples of client-side rendering. This is evident if you view the source of your Angular/React application.
But client-side rendering has the drawbacks that it doesn’t work great for SEO and that it returns incomplete HTML content when you share your link on social media sites. Angular has a solution called Angular Universal that takes care of making your app search engine friendly and social media friendly. It’s a library built by the Angular team, and using it is definitely favored.
Universal makes use of a pre-rendering technique where the whole website is rendered from the server first, and after a couple of seconds, the user is switched to the client-side rendering. Since all this happens under the hood, the user doesn’t notice anything different.
If you are using React with Redux, the Redux documentation has a good tutorial on setting up server rendering. You can also set up React to render from the server using the BrowserRouter
and StaticRouter
components available in the react-router
library. You can read more about it in this Medium article. But if you are into performance and optimization, you can try next.js, which is a library for SSR in React.
Wrapping It Up
Comparing a full-blown, feature-rich framework to a robust UI library might not seem fair. However, they are advanced JavaScript technologies used to create interactive single-page applications, and in that regard, this article should help you decide on choosing one of them.
What are your thoughts on Angular vs. React? Do share them in the comments below.