The Power of Server-Side Rendering with Angular Universal
The Power of Server-Side Rendering with Angular Universal
This article explores how we use Server-Side Rendering (SSR) with Angular Universal in this project to enhance SEO and improve performance.
What is SSR and Why is it Important?
Server-Side Rendering (SSR) is a technique for rendering a client-side single-page application (SPA) on the server and sending a fully rendered page to the client. This is in contrast to a traditional SPA, where the browser downloads a minimal HTML page and then renders the application using JavaScript.
For a portfolio and blog site like this, SSR is crucial for two main reasons:
- Search Engine Optimization (SEO): Search engine crawlers can more easily index a fully rendered HTML page, which improves the site's visibility in search results.
- Performance: Users see a meaningful first paint of the application much faster, as they don't have to wait for the JavaScript to download and execute.
As detailed in our ADR on SSR with Angular, we chose Angular Universal to get the best of both worlds: the rich interactivity of an SPA and the SEO and performance benefits of a server-rendered application.
How SSR Works in Our Project
Our Angular application is configured to be built in two main parts:
- A browser bundle: The standard Angular application that runs in the user's browser.
- A server bundle: A version of the application that can be run on a Node.js server.
When a user requests a page, our Node.js server uses the server bundle to render the requested route into static HTML and sends it to the user. The browser then downloads the browser bundle in the background and "hydrates" the static HTML, taking over and turning it into a fully interactive SPA.
The Key Files
Several files in the apps/mysite-frontend directory are key to our SSR implementation:
server.ts: This is the heart of our SSR setup. It's a Node.js Express server that handles incoming requests. It uses the@angular/ssrlibrary to create an Angular engine that renders the application. It also serves static files and has a health check endpoint.main.server.ts: This is the main entry point for the server-side application. It bootstraps the Angular application using a server-specific configuration.app.config.server.ts: This file provides the server-specific application configuration, including theprovideServerRenderingfunction from@angular/ssrwhich enables the SSR capabilities.
The Build Process
The project.json file for our frontend application contains the build configuration for SSR. The @angular/build:application executor is configured with "outputMode": "server", which tells the Angular CLI to produce both a browser and a server build.
When we run npx nx build mysite-frontend, the CLI creates a dist/apps/mysite-frontend directory with browser and server subdirectories, containing the respective bundles.
Containerizing the SSR App with Docker
To run our SSR application in production, we containerize it using Docker. Our Dockerfile.prod is a multi-stage Dockerfile that is optimized for security and a small image size.
Here are the key aspects of our Dockerfile:
- Builder Stage: We use a
nodeimage to build the application. We copy over the necessary files, install dependencies, and run thenx buildcommand. This stage contains all the development dependencies and build tools. - Runner Stage: We use a slim
nodeimage for the final production image. We create a non-root user (nodejs) for security. We then copy only the built application from thebuilderstage into therunnerstage. This results in a much smaller and more secure production image. - Running the server: The
CMDinstruction starts the Node.js server, which in turn serves our server-rendered Angular application.
Conclusion
By using Angular Universal for Server-Side Rendering, we get a fast, SEO-friendly, and modern web application. The setup is robust, and the use of Docker allows us to deploy it consistently and securely. This approach is a great example of how to build enterprise-grade Angular applications that are both performant and discoverable.