En esta página

SSR, pre-rendering y despliegue

15 min lectura TextoCap. 5 — Producción

Qué es SSR?

Server-Side Rendering (SSR) genera el HTML de tu aplicación en el servidor antes de enviarlo al navegador. Beneficios:

  • SEO mejorado — Los bots de busqueda leen HTML completo
  • Primera carga rápida — El usuario ve contenido inmediatamente
  • Social sharing — Meta tags disponibles para Open Graph

Modos de renderizado en Angular 21

Angular 21 define tres modos en ServerRoute:

Modo Descripcion Cuando usar
RenderMode.Prerender HTML generado en build time Páginas estaticas (home, about, blog)
RenderMode.Server HTML generado por request Páginas dinamicas (perfiles, busqueda)
RenderMode.Client Solo renderizado en el navegador Dashboard, admin, contenido privado

Configurar SSR

Al crear un proyecto con ng new, Angular pregunta si quieres SSR. Si ya tienes un proyecto, agregalo con:

ng add @angular/ssr

Esto genera:

  • src/app/app.config.server.ts — Configuración del servidor
  • src/app/app.routes.server.ts — Rutas con modos de renderizado
  • server.ts — Servidor Express

Rutas del servidor

Define como se renderiza cada ruta:

import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  // Pre-renderizar en build
  { path: '', renderMode: RenderMode.Prerender },
  { path: 'blog', renderMode: RenderMode.Prerender },

  // Pre-renderizar con parametros (genera un HTML por slug)
  {
    path: 'blog/:slug',
    renderMode: RenderMode.Prerender,
    async getPrerenderParams() {
      return [
        { slug: 'introduccion-angular' },
        { slug: 'signals-tutorial' },
        { slug: 'ssr-guia-completa' },
      ];
    },
  },

  // SSR dinámico
  { path: 'buscar', renderMode: RenderMode.Server },

  // Solo cliente
  { path: 'dashboard/**', renderMode: RenderMode.Client },
];

Deteccion de plataforma

Algunos APIs solo existen en el navegador. Usa isPlatformBrowser para proteger ese código:

import { isPlatformBrowser, PLATFORM_ID } from '@angular/common';
import { inject } from '@angular/core';

const platformId = inject(PLATFORM_ID);

if (isPlatformBrowser(platformId)) {
  // Seguro: estamos en el navegador
  window.scrollTo(0, 0);
  localStorage.setItem('key', 'value');
}

afterNextRender y afterRender

Para código que debe ejecutarse solo despues del renderizado en el navegador:

import { afterNextRender } from '@angular/core';

constructor() {
  afterNextRender(() => {
    // Se ejecuta una vez, solo en el navegador
    // Ideal para inicializar librerias de terceros
    this.chart = new Chart(this.canvasRef.nativeElement, config);
  });
}

Meta tags para SEO

Usa los servicios Title y Meta para establecer meta tags server-side:

import { Title, Meta } from '@angular/platform-browser';

@Component({ /* ... */ })
export class BlogPost {
  private readonly title = inject(Title);
  private readonly meta = inject(Meta);

  constructor() {
    effect(() => {
      const post = this.post();
      this.title.setTitle(`${post.título} | Mi Blog`);
      this.meta.updateTag({ name: 'description', content: post.resumen });
      this.meta.updateTag({ property: 'og:title', content: post.título });
      this.meta.updateTag({ property: 'og:image', content: post.imagen });
    });
  }
}

Despliegue

Firebase Hosting (estático + Cloud Functions)

ng build
firebase deploy

Docker (SSR completo)

FROM node:20-alpine
WORKDIR /app
COPY dist/ ./dist/
COPY package.json ./
RUN npm install --production
EXPOSE 4000
CMD ["node", "dist/mi-app/server/server.mjs"]

Plataformas compatibles

  • Firebase Hosting — Pre-rendering estático + Cloud Functions
  • Vercel — SSR serverless automático
  • Netlify — Pre-rendering estático
  • Railway / Render — SSR con Docker

Práctica

  1. Configura modos de renderizado: Define un archivo app.routes.server.ts con al menos 3 rutas usando los tres modos: Prerender para la pagina principal, Server para rutas dinamicas y Client para el dashboard.
  2. Protege APIs del navegador: Crea un servicio que use localStorage. Envuelvelo con isPlatformBrowser() para que no falle durante SSR y agrega un fallback para el servidor.
  3. Agrega meta tags SEO: Usa los servicios Title y Meta dentro de un effect() para establecer el titulo de la pagina y las etiquetas description y og:title dinamicamente en un componente.

En la siguiente leccion aprenderemos los fundamentos de testing en Angular con Vitest.

RenderMode
Usa Prerender para páginas estaticas (mejor rendimiento), Server para páginas dinamicas con datos frescos, y Client para secciones que requieren autenticación o APIs del navegador.
APIs del navegador
En SSR, window, document, localStorage y sessionStorage no existen. Usa isPlatformBrowser() o afterNextRender() para ejecutar código que dependa del navegador.
// --- app.config.server.ts ---
import { ApplicationConfig, mergeApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { provideServerRoutesConfig } from '@angular/ssr';
import { serverRoutes } from './app.routes.server';
import { appConfig } from './app.config';

const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering(),
    provideServerRoutesConfig(serverRoutes),
  ],
};

export default mergeApplicationConfig(appConfig, serverConfig);

// --- app.routes.server.ts ---
import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  // Páginas estaticas: se pre-renderizan en build
  { path: '', renderMode: RenderMode.Prerender },
  { path: 'blog', renderMode: RenderMode.Prerender },
  { path: 'cursos', renderMode: RenderMode.Prerender },

  // Páginas con parametros: SSR en el servidor
  {
    path: 'blog/:slug',
    renderMode: RenderMode.Server,
  },

  // Dashboard: solo cliente (no SSR)
  { path: 'dashboard/**', renderMode: RenderMode.Client },

  // Ruta catch-all
  { path: '**', renderMode: RenderMode.Server },
];