Deploy Angular Universal to Vercel

Learn how to make angular universal run on vercel as edge function

Deploy Angular Universal to Vercel

Getting started

Create new angular 16 app with standalone apis, use --minimal to skip routing.

$ ng new angular-vercel --minimal --routing --standalone --style=scss

CREATE angular-vercel/package.json (790 bytes)
CREATE angular-vercel/README.md (1067 bytes)
CREATE angular-vercel/tsconfig.json (901 bytes)
CREATE angular-vercel/.gitignore (548 bytes)
CREATE angular-vercel/tsconfig.app.json (263 bytes)
CREATE angular-vercel/.vscode/extensions.json (130 bytes)
CREATE angular-vercel/.vscode/launch.json (297 bytes)
CREATE angular-vercel/.vscode/tasks.json (531 bytes)
CREATE angular-vercel/src/main.ts (250 bytes)
CREATE angular-vercel/src/favicon.ico (1642 bytes)
CREATE angular-vercel/src/index.html (299 bytes)
CREATE angular-vercel/src/styles.scss (80 bytes)
CREATE angular-vercel/src/app/app.component.ts (1647 bytes)
CREATE angular-vercel/src/app/app.config.ts (228 bytes)
CREATE angular-vercel/src/app/app.routes.ts (77 bytes)
CREATE angular-vercel/src/assets/.gitkeep (0 bytes)
√ Packages installed successfully.

Add ssr

Install Angular Universal's express-engine packages

$ cd angular-vercel
$ ng add @nguniversal/express-engine

i Using package manager: yarn
√ Found compatible package version: @nguniversal/express-engine@16.0.2.
√ Package information loaded.
The package @nguniversal/express-engine@16.0.2 will be installed and executed.
Would you like to proceed? Yes
√ Packages successfully installed.

CREATE src/main.server.ts (264 bytes)
CREATE src/app/app.config.server.ts (350 bytes)
CREATE tsconfig.server.json (272 bytes)
CREATE server.ts (2002 bytes)
UPDATE package.json (1231 bytes)
UPDATE angular.json (5021 bytes)
√ Packages installed successfully.

Add vercel

Create vercel.json file in root directory of your project. We will redirect all requests to api directory which will export default an express app which will be imported from main server bundle.

"./vercel.json"

{
  "version": 2,
  "public": true,
  "name": "angular-vercel",
  "rewrites": [
    { "source": "/(.*)", "destination": "/api" }
  ],
  "functions": {
    "api/index.js": {
      "includeFiles": "dist/angular-vercel/browser/**"
    }
  }
}
"./api/index.js"

const server = require('../dist/angular-vercel/server/main');

module.exports = server.app();

Modify package.json

Add vercel-build script so that Vercel prioritize this command over yarn build.

"./package.json"

"scripts": {
  "ng": "ng",
  "start": "ng serve",
  "build": "ng build",
  "watch": "ng build --watch --configuration development",
  "dev:ssr": "ng run angular-vercel:serve-ssr",
  "serve:ssr": "node dist/angular-vercel/server/main.js",
  "build:ssr": "ng build && ng run angular-vercel:server",
  "prerender": "ng run angular-vercel:prerender",
  "vercel-build": "yarn build:ssr" <- add this script
},

Finishing

Before we deploy, create an empty folder public at root of your project with .gitkeep file inside.

$ touch public/.gitkeep

Generate two routes in angular application to test ssr working with different routes. Add these two components to your routing config.

$ ng g c routes/home
$ ng g c routes/about
"./src/app/app.routes.ts"

import { Routes } from '@angular/router';
import { HomeComponent } from './routes/home/home.component';

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
  },
  {
    path: 'about',
    loadComponent: () => import('./routes/about/about.component'),
  }
];
"./src/app/app.component.ts"

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [
    RouterOutlet,
    RouterLink,
  ],
  template: `
    <nav>
      <a routerLink="/">Home</a>
      <a routerLink="/about">About</a>
    </nav>
    <router-outlet></router-outlet>
  `,
})
export class AppComponent {

}

You can optionally add Angular 16's Hydration feature by use provideClientHydration function in your app.config.ts file.

"./src/app/app.config.ts"

import { provideClientHydration } from '@angular/platform-browser';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideClientHydration(),
  ]
};

Deploying

Install Vercel CLI using yarn and run vercel deploy

$ yarn global add vercel
$ vercel deploy
Vercel CLI 30.0.0
? Set up and deploy “X:\Development\projects\angular-vercel”? [Y/n] y
? Link to existing project? [y/N] n
? What’s your project’s name? angular-vercel
? In which directory is your code located? ./
Local settings detected in vercel.json:
Auto-detected Project Settings (Angular):
- Build Command: ng build
- Development Command: ng serve --port $PORT
- Install Command: `yarn install`, `pnpm install`, or `npm install`
- Output Directory: dist
? Want to modify these settings? [y/N] n
🔗  Linked to [name]/angular-vercel (created .vercel and added it to .gitignore)
🔍  Inspect: https://vercel.com/[name]/angular-vercel/72TnZdRH82ozigY544VWXWCRPgJz [1s]
✅  Production: https://angular-vercel-gamma-rosy.vercel.app [2m]
📝  Deployed to production. Run `vercel --prod` to overwrite later (https://vercel.link/2F).
💡  To change the domains or build command, go to https://vercel.com/[name]/angular-vercel/settings

That's it! Now you can see your angular universal app is fully server-side rendered using vercel edge functions.

go head and try: https://univercel.vercel.app