Random Quote App - Angular vs. React vs. Vue Comparison
Tags: angular, react, vue
Published: 2021-03-09 09:36:01

---
title: Random Quote App - Angular vs. React vs. Vue Comparison
published: true
description: Comparing the same app across Angular, Recat, and Vue
tags: angular, react, vue
cover_image: https://corscheid.dev/images/fcc-random-quote-machine-comparison.png
series: Quotes App
---

Originally posted on [corscheid.dev](https://corscheid.dev/blog/random-quote-app-comparison)

In the last post, we looked at how to build a simple random quote app in Angular. In this post, we will compare the same app in React and Vue, to have a look at the similarities and differences in Angular, React, and Vue. We'll cover the project structure, and tooling of each of these technologies, as well as how components are implemented in each.

## Contents

- [Links](#links)
- [Tooling](#tooling)
- [Project Structure](#project-structure)
- [Components](#components)
  - [Layouts](#layouts)
    - [App Layout](#app-layout)
    - [QuoteBox Layout](#quotebox-layout)
  - [Styles](#styles)
    - [Global Styles](#global-styles)
    - [App Styles](#app-styles)
    - [QuoteBox Styles](#quotebox-styles)
    - [Using Stylesheets](#using-stylesheets)
  - [Logic](#logic)
    - [App Logic](#app-logic)
    - [QuoteBox Logic](#quotebox-logic)
- [Impressions](#Impressions)

## Links

All three versions of the random quotes app are available on my GitHub.

- &rarr; [Angular repo](https://github.com/corscheid/fcc-random-quote-machine-angular)
- &rarr; [React repo](https://github.com/corscheid/fcc-random-quote-machine-react)
- &rarr; [Vue repo](https://github.com/corscheid/fcc-random-quote-machine-vue)

Following are some Documentation links if you want to check these technologies out in more detail or for how to get a project started with each, etc.

- &rarr; [angular.io](https://angular.io)
- &rarr; [reactjs.org](https://reactjs.org)
- &rarr; [vuejs.org](https://vuejs.org)

## Tooling

Each of these three has an associated command line tool that can be used to generate an initial project with all the boilerplate code for getting everything up and running quickly. For Angular, this is the `ng` command (Angular CLI), for React it's `create-react-app`, and for Vue, it's the `vue` command (Vue CLI). Here's a quick rundown with some examples.

Creating a new project called `my-app`:

```zsh
# Angular
ng new my-app

# React
npx create-react-app my-app

# Vue
vue create my-app
```

Documentation links for these tools:

- &rarr; [Angular CLI](https://cli.angular.io)
- &rarr; [Vue CLI](https://cli.vuejs.org)
- &rarr; [Create React App](https://create-react-app.dev)

## Project Structure

Angular seems to create a lot more files and directories by default when generating a project using `ng new` than React with `create-react-app` or Vue with `vue create`. Vue creates the lowest number of files and directories.

Note: The following project file trees displayed are all excluding the `node_modules/` and `.git/` directories, for sake of brevity.

**Angular**

Upon generating a new project with the Angular CLI (`ng new`), the following tree of directories and files is created.

```text
fcc-random-quote-machine-angular
├── .browserslistrc
├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── e2e/
│   ├── protractor.conf.js
│   ├── src/
│   │   ├── app.e2e-spec.ts
│   │   └── app.po.ts
│   └── tsconfig.json
├── karma.conf.js
├── package-lock.json
├── package.json
├── src/
│   ├── app/
│   │   ├── app.component.html
│   │   ├── app.component.scss
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   └── app.module.ts
│   ├── assets/
│   │   └── .gitkeep
│   ├── environments/
│   │   ├── environment.prod.ts
│   │   └── environment.ts
│   ├── favicon.ico
│   ├── index.html
│   ├── main.ts
│   ├── polyfills.ts
│   ├── styles.scss
│   └── test.ts
├── tsconfig.app.json
├── tsconfig.json
├── tsconfig.spec.json
└── tslint.json

6 directories, 30 files
```

**React**

Create React App generates the following.

```text
fcc-random-quote-machine-react
├── .gitignore
├── README.md
├── package.json
├── public/
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src/
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   ├── reportWebVitals.js
│   └── setupTests.js
└── yarn.lock

2 directories, 18 files
```

**Vue**

And Vue CLI (`vue create`) generates the following.

```text
fcc-random-quote-machine-vue
├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── README.md
├── babel.config.js
├── package.json
├── public/
│   ├── favicon.ico
│   └── index.html
├── src/
│   ├── App.vue
│   ├── assets/
│   │   └── logo.png
│   ├── components/
│   │   └── HelloWorld.vue
│   └── main.js
└── yarn.lock

4 directories, 13 files
```

## Components

Angular, React, and Vue are all component-based. The UI of an app is typically broken down into smaller components.

### Layouts

Within a component, there is generally some sort of layout information associated, as to how it should be displayed in the browser. As you'll see in the following sections, Angular and Vue use HTML templates, and React uses either functions that return JSX or classes with a `render()` method that returns JSX. JSX is a sort of XML-in-JavaScript syntax; you can read more about JSX on the [React Documentation Page](https://reactjs.org/docs/introducing-jsx.html). Essentially it allows the use of HTML tag-like syntax inside of JavaScript in order to make a readable template without needing to use a separate HTML file or interpolated template string.

#### App Layout

Here we'll take a look at the main layout structure in each. You'll notice that they are very similar. Comparing things at this level makes the difference between these technologies appear to be mostly that of syntax.

For example, in Angular, interpolating variable values from the TypeScript is done with "mustache" double braces `{{ }}`, and Vue does it the same way, but with React, which is typically written with JSX, we see single braces `{ }`.

Event handler binding in Angular such as `onClick` is written with `(click)=""`, where the JavaScript expression executed on the click is placed in the quotes. In Vue, it's the same idea with `@click=""`, which is shorthand for `v-on:click=""`. In React, it's `onClick={}`, which is a prop passed down to the component and the JS expression is placed between the single braces.

Binding is how HTML element attributes and the corresponding component class variables are kept in sync with each other when a change happens in either direction. In Angular, an example for the syntax for this would be `[tweetURL]="tweetURL"` (as seen in the following code snippets). Square brackets are used around the attribute name to signify that it is to be bound to a variable of the associated class, and in the quotes goes the variable it is bound to. In Vue, we have the same idea going on with `:tweetURL="tweetURL"`, which is short for `v-bind:tweetURL="tweetURL"`. These are somewhat similar to how React passes props down to child components, with the `tweetURL={tweetURL}` attribute-like syntax in JSX, but work differently under the hood.

**Angular**

```html
<!-- src/app/app.component.html (Angular) -->
<div *ngIf="loading; else content" id="loading">
  <h1>loading...</h1>
</div>
<ng-template #content>
  <app-quote-box
    [author]="quote.author"
    [quote]="quote.quote"
    [tweetURL]="tweetURL"
    [getNewQuote]="getNewQuote"
  ></app-quote-box>
</ng-template>
```

**React**

```javascript
// src/App.js – App function return statement (React)
return loading ? (
  <div id="loading">
    <h1>loading...</h1>
  </div>
) : (
  <QuoteBox
    quote={quote.quote}
    author={quote.author}
    getNewQuote={getNewQuote}
    tweetURL={tweetURL}
  />
);
```

**Vue**

```html
<!-- src/App.vue – template section (Vue) -->
<template>
  <div id="app">
    <div v-if="loading" id="loading">
      <h1>loading...</h1>
    </div>
    <QuoteBox
      v-else
      :quote="quote.quote"
      :author="quote.author"
      :tweetURL="tweetURL"
      :getNewQuote="getNewQuote"
    ></QuoteBox>
  </div>
</template>
```

#### QuoteBox Layout

Again, everything is almost the same, except some bits of syntax.

**Angular**

```html
<!-- `src/app/quote-box/quote-box.component.html` (Angular) -->
<div id="quote-box">
  <h1 id="text"><i class="fa fa-quote-left"></i> {{ quote }}</h1>
  <p id="author">- {{ author }}</p>
  <div class="btn-row">
    <button class="btn btn-primary" id="new-quote" (click)="getNewQuote()">
      New quote
    </button>
    <a
      id="tweet-quote"
      href="{{ tweetURL }}"
      target="_top"
      class="btn btn-secondary"
    >
      <i class="fa fa-twitter"></i> Tweet
    </a>
  </div>
</div>
```

**React**

```javascript
// src/components/QuoteBox.js – QuoteBox function return statement (React)
return (
  <div id="quote-box">
    <h1 id="text">
      <i className="fa fa-quote-left"></i> {props.quote}
    </h1>
    <p id="author">- {props.author}</p>
    <div className="btn-row">
      <button
        className="btn btn-primary"
        id="new-quote"
        onClick={props.getNewQuote}
      >
        New quote
      </button>
      <a
        id="tweet-quote"
        href={props.tweetURL}
        target="_top"
        className="btn btn-secondary"
      >
        <i className="fa fa-twitter"></i> Tweet
      </a>
    </div>
  </div>
);
```

**Vue**

```html
<!-- src/components/QuoteBox.vue – template section (Vue) -->
<template>
  <div id="quote-box">
    <h1 id="text"><i class="fa fa-quote-left"></i> {{ quote }}</h1>
    <p id="author">- {{ author }}</p>
    <div class="btn-row">
      <button class="btn btn-primary" id="new-quote" @click="getNewQuote()">
        New quote
      </button>
      <a
        id="tweet-quote"
        href="tweetURL"
        target="_top"
        class="btn btn-secondary"
      >
        <i class="fa fa-twitter"></i> Tweet
      </a>
    </div>
  </div>
</template>
```

### Styles

The same Sass styles were used in each version of this app. The only differences that occur are in the mechanisms for how global styles and component-specific styles are applied.

#### Global Styles

The global sass stylesheet is the same in all three, except that the filepaths / filenames differ.

**Angular, React, and Vue**

```scss
/* src/styles.scss (Angular) */
/* src/index.scss (React) */
/* src/styles/styles.scss (Vue) */

/* Bootstrap 5 */
@import url("https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css");
/* Font Awesome */
@import url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css");
/* Google Fonts */
@import url("https://fonts.googleapis.com/css2?family=Amiri&family=Indie+Flower&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Neucha&display=swap");

$blue: #58f;

html,
body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}

#root {
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: $blue;
  height: 100%;
  overflow-y: hidden;
}
```

#### App Styles

Here are the styles for the main app component.

**Angular & React**

```scss
/* src/app/app.component.scss (Angular) */
/* src/App.scss (React) */
$white: #fafafa;

#loading {
  color: $white;
  font-family: "Amiri", serif;
}
```

**Vue**

In Vue, styles go inside a `style` section at the bottom of the component file.

```html
<!-- src/App.vue – style section (Vue) -->
<style lang="scss">
  $white: #fafafa;

  #loading {
    color: $white;
    font-family: "Amiri", serif;
  }
</style>
```

#### QuoteBox Styles

Here are the styles for the QuoteBox component.

**Angular & React**

```scss
/* src/app/quote-box/quote-box.component.scss (Angular) */
/* src/components/QuoteBox.scss (React) */
$black: #3f3f3f;
$white: #fafafa;

#quote-box {
  padding: 2em;
  background-color: $white;
  margin: 20%;
  border-radius: 10px;
  color: $black;

  #text {
    font-family: "Amiri", serif;
  }
  #author {
    font-family: "Neucha", cursive;
    font-size: 2.5em;
  }
  .btn-row {
    display: flex;
    flex-direction: row;
    justify-content: flex-end;

    #tweet-quote {
      margin-left: 1em;
    }
  }
}

@media only screen and (max-width: 480px) {
  #quote-box {
    margin: 0;
    overflow-y: auto;
  }
}
```

**Vue**

```html
<!-- src/components/QuoteBox.vue – style section (Vue) -->
<style lang="scss" scoped>
  $white: #fafafa;
  $black: #3f3f3f;
  #quote-box {
    padding: 2em;
    background-color: $white;
    margin: 20%;
    border-radius: 10px;
    color: $black;
    #text {
      font-family: "Amiri", serif;
    }
    #author {
      font-family: "Neucha", cursive;
      font-size: 2.5em;
    }
    .btn-row {
      display: flex;
      flex-direction: row;
      justify-content: flex-end;
      #tweet-quote {
        margin-left: 1em;
      }
    }
  }
  @media only screen and (max-width: 480px) {
    #quote-box {
      margin: 0;
      overflow-y: auto;
    }
  }
</style>
```

#### Using Stylesheets

**Angular**

In Angular, component-specific stylesheets are their own separate files within a component directory, and imported via the `@Component()` decorator `styleUrls` property inside the component's TypeScript (`.ts`) file. This decorator and its properties will be automatically generated by the Angular CLI when using `ng new` or `ng generate component`.

```typescript
// src/app/app.component.ts (Angular)
// ...

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"],
})
export class AppComponent implements OnInit {
  // ...
}
```

```typescript
// src/app/quote-box/quote-box.component.ts (Angular)
// ...

@Component({
  selector: "app-quote-box",
  templateUrl: "./quote-box.component.html",
  styleUrls: ["./quote-box.component.scss"],
})
export class QuoteBoxComponent {
  // ...
}
```

The global stylesheet at `src/styles.scss` in Angular seems to be automatically imported and applied at the app level without any modifications to the app module or component code.

**React**

In React, component-specific stylesheets can just be imported into the component JavaScript file just like a typical JavaScript import.

```javascript
// src/App.js (React)
import React from "react";
import QuoteBox from "./components/QuoteBox";
import "./App.scss";

const App = () => {
  // ...
};
```

```javascript
// src/components/QuoteBox.js (React)
import "./QuoteBox.scss";

const QuoteBox = (props) => {
  // ...
};
```

The global stylesheet at `src/index.scss` is imported at the top of `src/index.js`.

```javascript
// src/index.js (React)
import React from "react";
import ReactDOM from "react-dom";
import "./index.scss"; // <-- import global stylesheet here
import App from "./App";
import reportWebVitals from "./reportWebVitals";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);
// ...
```

**Vue**

As seen previously, component-specific styles in Vue are placed inside `style` tags at the bottom of a `.vue` component file. The contents there aren't imported by the JavaScript in the `script` tag section, and seem to automatically be applied to the component.

Global stylesheets, on the other hand are imported much like in Angular and React. It will be imported in `src/main.js` like so:

```javascript
// src/main.js (Vue)
import Vue from "vue";
import App from "./App.vue";
import "./styles/styles.scss"; // <-- import global stylesheet here

Vue.config.productionTip = false;

new Vue({
  render: (h) => h(App),
}).$mount("#app");
```

### Logic

App logic in Angular is handled in TypeScript, and in the other two with JavaScript, with the option of adding TypeScript if desired. For these I chose the default route of using JavaScript, but it's fairly easy to switch to TypeScript with either React or Vue.

#### App Logic

**Angular**

With Angular, the application logic resides in the `AppComponent` class inside `src/app.component.ts`.

```typescript
// src/app/app.component.ts (Angular)
import { Component, OnInit } from "@angular/core";

interface Quote {
  quote: string;
  author: string;
}

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.scss"],
})
export class AppComponent implements OnInit {
  loading: boolean = true;
  quote!: Quote;
  quoteList!: Quote[];
  tweetURL!: string;
  getNewQuote: () => void = (): void => {
    const idx = Math.floor(Math.random() * this.quoteList.length);
    const newQuote = this.quoteList[idx];
    this.quote = newQuote;
  };

  constructor() {}

  ngOnInit() {
    this.fetchData();
  }

  async fetchData(): Promise<void> {
    const quotesURL =
      "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";
    const response = await fetch(quotesURL);
    const quotes = await response.json();
    const idx = Math.floor(Math.random() * quotes.quotes.length);
    const newQuote = quotes.quotes[idx];
    this.quoteList = quotes.quotes;
    this.quote = newQuote;
    this.setTweetURL(newQuote);
    this.loading = false;
  }

  setTweetURL(quote: Quote): void {
    this.tweetURL = `https://twitter.com/intent/tweet?hashtags=quotes&related=freecodecamp&text=${quote.quote} --${quote.author}`;
  }
}
```

**React**

In React, it's either a function or class inside `src/App.js`. In this case, it's the `App` arrow function there.

```javascript
// src/App.js (React)
import React from "react";
import QuoteBox from "./components/QuoteBox";
import "./App.scss";

const App = () => {
  const [loading, setLoading] = React.useState(true);
  const [quote, setQuote] = React.useState({});
  const [quoteList, setQuoteList] = React.useState([]);
  const [tweetURL, setTweetURL] = React.useState("");

  const getNewQuote = () => {
    const idx = Math.floor(Math.random() * quoteList.length);
    const newQuote = quoteList[idx];
    setQuote(newQuote);
  };

  const fetchData = async () => {
    const quotesURL =
      "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";
    const response = await fetch(quotesURL);
    const quotes = await response.json();
    const idx = Math.floor(Math.random() * quotes.quotes.length);
    const newQuote = quotes.quotes[idx];
    setQuoteList(quotes.quotes);
    setQuote(newQuote);
    setTweetURL(
      `https://twitter.com/intent/tweet?hashtags=quotes&related=freecodecamp&text=${newQuote.quote} --${newQuote.author}`
    );
    setLoading(false);
  };

  React.useEffect(() => {
    fetchData();
  }, []);

  return loading ? (
    <div id="loading">
      <h1>loading...</h1>
    </div>
  ) : (
    <QuoteBox
      quote={quote.quote}
      author={quote.author}
      getNewQuote={getNewQuote}
      tweetURL={tweetURL}
    />
  );
};

export default App;
```

**Vue**

In Vue, it's the `script` tag section of `src/App.vue`.

```html
<!-- src/App.vue – script section (Vue) -->
<script>
  import QuoteBox from "./components/QuoteBox.vue";
  export default {
    name: "App",
    components: {
      QuoteBox,
    },
    data() {
      return {
        loading: true,
        quote: {},
        quoteList: [],
        tweetURL: "",
      };
    },
    created() {
      this.fetchData();
    },
    methods: {
      async fetchData() {
        const quotesURL =
          "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json";
        const response = await fetch(quotesURL);
        const quotes = await response.json();
        const idx = Math.floor(Math.random() * quotes.quotes.length);
        const newQuote = quotes.quotes[idx];
        this.quoteList = quotes.quotes;
        this.quote = newQuote;
        this.tweetURL = `https://twitter.com/intent/tweet?hashtags=quotes&related=freecodecamp&text=${newQuote.quote} --${newQuote.author}`;
        this.loading = false;
      },
      getNewQuote() {
        const idx = Math.floor(Math.random() * this.quoteList.length);
        const newQuote = this.quoteList[idx];
        this.quote = newQuote;
      },
    },
  };
</script>
```

#### QuoteBox Logic

There isn't really much "logic" in the QuoteBox component in any case; it's mostly just a display component defining the UI given some values from the parent app component.

**Angular**

For Angular, the QuoteBoxComponent class is defined in `src/app/quote-box/quote-box.component.ts`.

```typescript
import { Component, Input } from "@angular/core";

@Component({
  selector: "app-quote-box",
  templateUrl: "./quote-box.component.html",
  styleUrls: ["./quote-box.component.scss"],
})
export class QuoteBoxComponent {
  @Input() author!: string;
  @Input() quote!: string;
  @Input() tweetURL!: string;
  @Input() getNewQuote!: () => void;

  constructor() {}
}
```

Notice the `@Input()` decorator on each of these class variables. What this essentially means is a parent component will be providing values to these as inputs. Essentially all this class does is receive values from the parent and then inject them into the template due to the corresponding bindings.

**React**

In the React version of this project, the QuoteBox component logic is defined as a very simple arrow function in `src/components/QuoteBox.js`.

```javascript
// src/components/QuoteBox.js (React)
import "./QuoteBox.scss";

const QuoteBox = (props) => {
  return (
    <div id="quote-box">
      <h1 id="text">
        <i className="fa fa-quote-left"></i> {props.quote}
      </h1>
      <p id="author">- {props.author}</p>
      <div className="btn-row">
        <button
          className="btn btn-primary"
          id="new-quote"
          onClick={props.getNewQuote}
        >
          New quote
        </button>
        <a
          id="tweet-quote"
          href={props.tweetURL}
          target="_top"
          className="btn btn-secondary"
        >
          <i className="fa fa-twitter"></i> Tweet
        </a>
      </div>
    </div>
  );
};

export default QuoteBox;
```

The `props` parameter is essentially an object where the parent passes data down to a child. In the parent's JSX return statement, these values will appear like attributes assigned to either literal values or expression values. The QuoteBox function's returned JSX looks almost exactly like the layout templates in Angular and Vue. Again the only thing this function really does is serve to inject given prop values into a UI template defined by the JSX.

Unlike in Angular and Vue, where the component name is defined by initializing a string variable, the exported function or class name itself serves as the expected identifier of the component for use with other components' JSX in React.

**Vue**

In Vue, the QuoteBox is again very similar, and does basically the same exact thing, but with even less code, in the `script` section of `src/components/QuoteBox.vue`.

```html
<!-- src/components/QuoteBox.vue – script section (Vue) -->
<script>
  export default {
    name: "QuoteBox",
    props: {
      quote: String,
      author: String,
      tweetURL: String,
      getNewQuote: Function,
    },
  };
</script>
```

here we define a `props` object in a more traditional looking way that React does in JSX. The props seem to work a lot like in React. The parent will pass down these values from the template and logic. This component will just receive them as values and sync them with the component template. Again, just receiving the values from the parent app component and placing them in the QuoteBox template.

the `name` property here works pretty much exactly like the `selector` property of the `@Component()` decorator in Angular. It defines the expected name of this component for use in other Vue templates.

## Impressions

Overall, I found it fairly easy to get the basics down in all three of Angular, React, and Vue for a small project such as this. The documentation for all three is really good and well maintained. Each has its own strengths and weaknesses, but for a project like this one, I found that personally there is almost no difference in the learning curve and general ease of use. It's difficult for me to choose a favorite here, and I don't think that there are any "winners" or "losers", "better" or "worse". It comes down to what you're used to and what you like. Out of the three, I definitely have had much more exposure to React and like it a lot. But after using Angular and Vue, I really like them too, just about as much. I will be using Angular for work, so I thought it would be good to dive in and convert something familiar in React to Angular. And just for kicks, also to Vue, since I see that is rising rapidly in popularity these days.

My general impressions about the similarities and differences is that they are so similar, that it is in my opinion quite easy to go from using one to the other between the three. I'd almost go so far as to oversimplify and sum it up as "It's just syntax".

For the use cases, I would say that the impression I got from working with Angular is that it seems very much suited for large scale enterprise applications right out of the box. This isn't quite a fair comparison to make because Angular and Vue are fully considered to be _frameworks_ and React is a _library_. And, obviously, React is used in tons of large scale corporate projects (and I'm sure Vue is as well). React to me feels like a lightweight library for writing UIs quickly and easily, and it mostly stays out of the way, having no real opinions about structure, and allows for a lot of customizations. I've found that Angular and Vue seem to have opinions and more rigid structure, which probably comes from them being frameworks having established conventions more so than React.