Why Angular Universal?
Angular Universal (and server-side rendering (SSR) in general) offer several benefits for web development:
Improved SEO
Search engines prefer fully rendered HTML pages, and SSR allows for this. This means that your app will be more easily indexed and discovered by search engines.
Faster load times
With SSR, the server sends a fully rendered HTML page to the client, which means the client does not need to wait for the JavaScript code to download and execute before rendering the page. This can lead to significantly faster load times.
Improved performance on low-powered devices
Some devices, such as mobile phones and tablets, may not have the processing power to execute JavaScript quickly. With SSR, the client receives a fully rendered HTML page, which can improve performance on low-powered devices.
Improved first-time user experience
When a user visits your app for the first time, they will receive a fully rendered HTML page immediately, rather than having to wait for the client-side code to download and execute. This can improve the user experience and reduce bounce rates.
Better accessibility
Some users may have JavaScript disabled or may be using screen readers that do not support JavaScript. SSR ensures that all users can access your app's content, regardless of their device or assistive technology.
Overall, Angular Universal and SSR are powerful tools that can improve the performance, accessibility, and user experience of your app.
Building Your App
Prerequisites
Before diving into the creation of an Angular Universal app, it's important to have a basic understanding of Angular, Node.js, and TypeScript. If you're new to Angular, we recommend reading the official Angular documentation to get started.
Step 1: Create a new Angular app
The first step in creating an Angular Universal app is creating a new Angular app. To do this, open your terminal and run the following command:
ng new my-universal-app
This will create a new Angular app with the name my-universal-app
. After running this command, navigate to the project directory by running:
cd my-universal-app
Step 2: Add Angular Universal
The next step is to add Angular Universal to the app. This can be achieved by running the following command:
ng add @nguniversal/express-engine
This command will install the necessary packages and configure the app for server-side rendering. Express is one of the most popular Node.js web frameworks which allows us to handle HTTP requests and responses.
Step 3: Generate a Universal app
Now that Angular Universal has been added to the app, we can generate a Universal app. A Universal app is an app that can be rendered on both the server and client sides. To generate a Universal app, run the following command:
ng generate universal my-app
This will create a new app called my-app
. Open the my-app.component.ts
file and add the following code:
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-my-app', template: ` <div> <h1>Hello, World!</h1> <p>This is my first Universal app!</p> </div> `, }) export class MyAppComponent implements OnInit { constructor() {} ngOnInit(): void {} }
Step 4: Add the Universal app to the App
Now that we have a Universal app, we need to add it to the app. Open the app.module.ts
file and add the following code:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { MyAppComponent } from './my-app/my-app.component'; @NgModule({ declarations: [AppComponent, MyAppComponent], imports: [BrowserModule], providers: [], bootstrap: [AppComponent], }) export class AppModule {}
Here, we are importing BrowserModule
which is a module that provides services and directives for browser-based applications. AppComponent
is the main component of our app which will be bootstrapped when the app starts.
Step 5: Build the App
With the Universal app added to the app, we can now build the app for server-side rendering. To do this, run the following command:
npm run build:ssr
This will build the app, including the Universal app, for server-side rendering.
Step 6: Start the Server
The final step is to start the server and test the app. Run the following command to start the server:
npm run serve:ssr
This will start the server, and your app should now be accessible at http://localhost:4000
. Check the page source to ensure that the Universal app has been rendered on the server side.
Transferring State to the Client
When rendering Angular applications on the server-side, you can end up loading your data and external content twice. If using a CMS, API or database, every request you make is duplicated -once once on the server, and again in the client. If you pay per-request, or have limits set by your provider, this can get expensive. It can also cause a loading delay as the page starts up with all the content sent from the server, and then the client-side app requests it all again and replaces the content that was already rendered by the server. User’s can experience a “flash” as the client-side app takes over, as the content is removed and replaced exactly as it was.
Fortunately, Angular Universal ships with a very neat solution to this. When rendering Angular applications on the server, data can be transferred to the client using the TransferState
service. It works very simply by adding a JSON variable (which has any data you ask to be transferred in an encrypted state) to the bottom of the HTML page it sends to the client. You can then lookup data via the TransferState
service in your client-side app, and if it exists use this instead of making a new request.
To use the TransferState
service, we need to import it from @angular/platform-browser
in our Universal app:
import { Component, OnInit } from '@angular/core'; import { TransferState, makeStateKey } from '@angular/platform-browser'; @Component({ selector: 'app-my-app', template: ` <div> <h1>Hello, World!</h1> <p>This is my first Universal app!</p> </div> `, }) export class MyAppComponent implements OnInit { message: string; constructor(private state: TransferState) {} ngOnInit(): void { const MESSAGE_KEY = makeStateKey<string>('message'); const message = this.state.get(MESSAGE_KEY, ''); if (message) { this.message = message; } else { this.message = 'This is my first Universal app!'; this.state.set(MESSAGE_KEY, this.message); } } }
In this example, we are using TransferState
to transfer a message from the server to the client. We first create a MESSAGE_KEY
using makeStateKey
, and then use this.state.get
to get the message from TransferState
. If the message is not in TransferState
, we set it using this.state.set
.
Conclusion
In this article, we have covered the steps required to create your first Angular Universal app. We started by creating a new Angular app, then added Angular Universal, generated a Universal app, added the Universal app to the app, built the app, and started the server. We also covered how to use TransferState
to transfer data between the server and client when rendering Angular applications on the server-side. With these steps, you should now have a basic understanding of how to create an Angular Universal app. Happy coding!