DEV Community

Martin Choraine for Zenika

Posted on • Edited on

Angular Universal with Standalone Component

With the arrival of Standalone Component with Angular 15, it is now possible to do without @NgModules entirely. This opens the door to new possibilities.

It is now possible to bootstrap an Angular application by providing an entry point component with a list of providers in the main.ts :

bootstrapApplication(AppComponent, {
  providers: [
    provideAnimations(),
    provideRouter(routes)
  ]
})
Enter fullscreen mode Exit fullscreen mode

However, with this new approach, it is not yet possible to simply take advantage of the server side rendering (SSR) with Angular Universal because the function ngExpressEngine only takes a module parameter and not a component:

import express from 'express';
import { ngExpressEngine } from '@nguniversal/express-engine';

const app = express();

// Set the engine
app.engine(
  'html',
  // Bootstrap and render NgModule with express adapter for `@angular/platform-server`
  ngExpressEngine({
    // Take a module but we would like to provide component
    bootstrap: ServerAppModule
  }),
);

app.set('view engine', 'html');

app.get('/**/*', (req: Request, res: Response) => {
  res.render('../dist/index', {
    req,
    res,
  });
});
Enter fullscreen mode Exit fullscreen mode

On the other hand it is possible to use manually the render function provided by @angular/platform-server.
This forces us to redo the work done by Angular Universal inside ngExpressEngine but it's the only possibility while waiting for the support of standalone component :

import express from 'express';
import type { Request, Response } from 'express';
import * as fs from 'fs';
import { renderApplication } from '@angular/platform-server'

const app = express();

app.engine('html', async (path, options, callback) => {
    const document = fs.readFileSync(path, 'utf-8')
    const { req } = { ...options } as {req: Request, res: Response};

    // Bootstrap and render a Standalone Component 
    const html = await renderApplication(AppComponent, {
      appId: 'server-app',
      document: document,
      url: `${req.baseUrl}${req.url}`,
      providers: [
        provideRouter(routes),
      ]
    });
    callback(null, html)
});
app.set('view engine', 'html');

app.get('/**/*', (req: Request, res: Response) => {
  res.render('../dist/index', {
    req,
    res,
  });
});
Enter fullscreen mode Exit fullscreen mode

Thanks to the function renderApplication, it is possible to bootstrap an Angular component and render it on the server side while waiting for the full support of Standalone Component in Angular Universal with the provided function ngExpressEngine.

Latest comments (3)

Collapse
 
zuhairkareem profile image
zuhairkareem

It is now implemented in Angular v16 according to this (
github.com/angular/universal/pull/...)

ref: reddit.com/r/angular/comments/zqko...

Collapse
 
sabihope profile image
sabihope

Do we need to render each standalone component?

Collapse
 
vytautaspranskunas profile image
Vytautas Pranskunas

Would you mid sharing whole example?