> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angular/angular/llms.txt
> Use this file to discover all available pages before exploring further.

# @angular/platform-server

> Platform APIs for server-side rendering (SSR) of Angular applications

## Overview

The `@angular/platform-server` package provides the infrastructure for rendering Angular applications on the server. This enables server-side rendering (SSR) for improved performance, SEO, and social media sharing.

## Rendering Functions

### renderApplication

Bootstraps and renders a standalone Angular application to an HTML string. This is the recommended approach for SSR with standalone components.

```typescript theme={null}
function renderApplication<T>(
  bootstrap: (context: BootstrapContext) => Promise<ApplicationRef>,
  options: {
    document?: string | Document
    url?: string
    platformProviders?: Provider[]
  }
): Promise<string>
```

<ParamField path="bootstrap" type="(context: BootstrapContext) => Promise<ApplicationRef>" required>
  Function that bootstraps the application. Receives a `BootstrapContext` with the platform reference.
</ParamField>

<ParamField path="options" type="object" required>
  Configuration options for the render operation.
</ParamField>

<ParamField path="options.document" type="string | Document">
  The HTML document to render into, either as a string or Document instance. Defaults to a minimal HTML template.
</ParamField>

<ParamField path="options.url" type="string">
  The URL for the current render request. Used for routing and generating absolute URLs.
</ParamField>

<ParamField path="options.platformProviders" type="Provider[]">
  Additional platform-level providers for the render operation.
</ParamField>

**Returns:** `Promise<string>` - The rendered HTML as a string.

**Example:**

```typescript theme={null}
import { bootstrapApplication } from '@angular/platform-browser';
import { renderApplication } from '@angular/platform-server';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';

export async function render(url: string, document: string) {
  const html = await renderApplication(
    (context) => bootstrapApplication(AppComponent, {
      ...config,
      providers: [
        ...config.providers || []
      ]
    }, context),
    {
      document,
      url
    }
  );
  
  return html;
}
```

**Express Integration:**

```typescript theme={null}
import express from 'express';
import { renderApplication } from '@angular/platform-server';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './src/app/app.component';
import { readFileSync } from 'fs';
import { join } from 'path';

const app = express();
const indexHtml = readFileSync(join(process.cwd(), 'dist/browser/index.html'), 'utf-8');

app.get('*', async (req, res) => {
  try {
    const html = await renderApplication(
      (context) => bootstrapApplication(AppComponent, appConfig, context),
      {
        document: indexHtml,
        url: req.url
      }
    );
    
    res.send(html);
  } catch (error) {
    console.error('SSR error:', error);
    res.status(500).send('Server error');
  }
});

app.listen(4000);
```

<Note>
  `renderApplication` automatically waits for the application to stabilize before rendering. This ensures all async operations complete before generating the HTML.
</Note>

### renderModule

Bootstraps and renders an NgModule-based Angular application to an HTML string. Use this for applications using NgModules instead of standalone components.

```typescript theme={null}
function renderModule<T>(
  moduleType: Type<T>,
  options: {
    document?: string | Document
    url?: string
    extraProviders?: StaticProvider[]
  }
): Promise<string>
```

<ParamField path="moduleType" type="Type<T>" required>
  The NgModule class to bootstrap.
</ParamField>

<ParamField path="options" type="object" required>
  Configuration options for the render operation.
</ParamField>

<ParamField path="options.document" type="string | Document">
  The HTML document to render into.
</ParamField>

<ParamField path="options.url" type="string">
  The URL for the current render request.
</ParamField>

<ParamField path="options.extraProviders" type="StaticProvider[]">
  Additional platform-level providers.
</ParamField>

**Returns:** `Promise<string>` - The rendered HTML as a string.

**Example:**

```typescript theme={null}
import { renderModule } from '@angular/platform-server';
import { AppServerModule } from './app/app.server.module';
import { readFileSync } from 'fs';

const indexHtml = readFileSync('./dist/browser/index.html', 'utf-8');

export async function render(url: string) {
  const html = await renderModule(AppServerModule, {
    document: indexHtml,
    url,
    extraProviders: [
      // Additional providers if needed
    ]
  });
  
  return html;
}
```

## Platform

### platformServer

Creates a server platform instance for Angular applications. This is used internally by `renderApplication` and `renderModule` but can be used directly for advanced scenarios.

```typescript theme={null}
function platformServer(
  extraProviders?: StaticProvider[]
): PlatformRef
```

<ParamField path="extraProviders" type="StaticProvider[]">
  Additional platform-level providers.
</ParamField>

**Returns:** `PlatformRef` - Server platform reference.

**Example:**

```typescript theme={null}
import { platformServer } from '@angular/platform-server';
import { AppServerModule } from './app/app.server.module';

const platform = platformServer();
const moduleRef = await platform.bootstrapModule(AppServerModule);

// Access application services
const appRef = moduleRef.injector.get(ApplicationRef);
await appRef.whenStable();

// Clean up
platform.destroy();
```

### ServerModule

NgModule that provides server-specific implementations for Angular's browser APIs. Import this in your server-specific app module.

```typescript theme={null}
@NgModule({
  exports: [BrowserModule]
})
class ServerModule {}
```

**Example:**

```typescript theme={null}
import { NgModule } from '@angular/core';
import { ServerModule } from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
  imports: [
    AppModule,
    ServerModule
  ],
  bootstrap: [AppComponent]
})
export class AppServerModule {}
```

<Note>
  For standalone applications, use `provideServerRendering()` instead of `ServerModule`.
</Note>

## Providers

### provideServerRendering

Provides server-side rendering configuration for standalone applications. Use this in your server application configuration.

```typescript theme={null}
function provideServerRendering(): EnvironmentProviders
```

**Example:**

```typescript theme={null}
import { ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';

export const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering(),
    provideRouter(routes)
  ]
};
```

**Complete Server Setup:**

```typescript theme={null}
// app.config.server.ts
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';

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

export const config = mergeApplicationConfig(appConfig, serverConfig);
```

## Services

### PlatformState

Service that provides access to the server-rendered document and rendering utilities.

```typescript theme={null}
@Injectable()
class PlatformState {
  getDocument(): Document
  renderToString(): string
}
```

**Methods:**

<ParamField path="getDocument" type="() => Document">
  Returns the server-side Document instance.
</ParamField>

<ParamField path="renderToString" type="() => string">
  Renders the current document to an HTML string.
</ParamField>

**Example:**

```typescript theme={null}
import { Injectable, inject } from '@angular/core';
import { PlatformState } from '@angular/platform-server';

@Injectable()
export class ServerService {
  private platformState = inject(PlatformState);

  getDocumentTitle(): string {
    const doc = this.platformState.getDocument();
    return doc.title;
  }

  addMetaTag(name: string, content: string): void {
    const doc = this.platformState.getDocument();
    const meta = doc.createElement('meta');
    meta.setAttribute('name', name);
    meta.setAttribute('content', content);
    doc.head.appendChild(meta);
  }
}
```

## Configuration Tokens

### INITIAL\_CONFIG

Injection token for providing initial server configuration.

```typescript theme={null}
const INITIAL_CONFIG: InjectionToken<PlatformConfig>
```

**PlatformConfig Interface:**

```typescript theme={null}
interface PlatformConfig {
  document?: string | Document
  url?: string
}
```

**Example:**

```typescript theme={null}
import { INITIAL_CONFIG } from '@angular/platform-server';
import { platformServer } from '@angular/platform-server';

const platform = platformServer([
  {
    provide: INITIAL_CONFIG,
    useValue: {
      document: '<html><body><app-root></app-root></body></html>',
      url: 'https://example.com/page'
    }
  }
]);
```

### BEFORE\_APP\_SERIALIZED

Injection token for callbacks that run before the application is serialized to HTML. Useful for finalizing state or performing cleanup.

```typescript theme={null}
const BEFORE_APP_SERIALIZED: InjectionToken<Array<() => void | Promise<void>>>
```

**Example:**

```typescript theme={null}
import { BEFORE_APP_SERIALIZED } from '@angular/platform-server';
import { Provider } from '@angular/core';

export const serverCallbackProvider: Provider = {
  provide: BEFORE_APP_SERIALIZED,
  useFactory: () => {
    return () => {
      console.log('About to serialize application');
      // Perform cleanup or state finalization
    };
  },
  multi: true
};
```

**Async Callbacks:**

```typescript theme={null}
import { BEFORE_APP_SERIALIZED } from '@angular/platform-server';
import { inject } from '@angular/core';
import { DataService } from './data.service';

export const asyncCallbackProvider = {
  provide: BEFORE_APP_SERIALIZED,
  useFactory: () => {
    const dataService = inject(DataService);
    return async () => {
      // Wait for pending operations
      await dataService.flushCache();
    };
  },
  multi: true
};
```

## Server-Side Rendering Setup

### Basic SSR Setup

For a complete SSR setup with standalone components:

**1. Install dependencies:**

```bash theme={null}
ng add @angular/ssr
```

**2. Create server configuration (app.config.server.ts):**

```typescript theme={null}
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { appConfig } from './app.config';

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

export const config = mergeApplicationConfig(appConfig, serverConfig);
```

**3. Create server entry point (main.server.ts):**

```typescript theme={null}
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { config } from './app/app.config.server';

const bootstrap = (context) => bootstrapApplication(AppComponent, config, context);

export default bootstrap;
```

**4. Create Express server:**

```typescript theme={null}
import express from 'express';
import { renderApplication } from '@angular/platform-server';
import bootstrap from './main.server';
import { readFileSync } from 'fs';

const app = express();
const indexHtml = readFileSync('./dist/browser/index.html', 'utf-8');

app.get('*', async (req, res) => {
  const html = await renderApplication(bootstrap, {
    document: indexHtml,
    url: req.url
  });
  res.send(html);
});

app.listen(4000, () => {
  console.log('Server running on http://localhost:4000');
});
```

### Transfer State

Transfer state allows data to be shared between server and client, avoiding duplicate HTTP requests.

```typescript theme={null}
import { ApplicationConfig } from '@angular/core';
import { provideClientHydration } from '@angular/platform-browser';
import { provideServerRendering } from '@angular/platform-server';

// Client config
export const appConfig: ApplicationConfig = {
  providers: [
    provideClientHydration()
  ]
};

// Server config
export const serverConfig: ApplicationConfig = {
  providers: [
    provideServerRendering()
  ]
};
```

**Using TransferState Service:**

```typescript theme={null}
import { Component, inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { TransferState, makeStateKey } from '@angular/core';
import { HttpClient } from '@angular/common/http';

const DATA_KEY = makeStateKey<any>('userData');

@Component({
  selector: 'app-user',
  template: '<div>{{ userData | json }}</div>'
})
export class UserComponent {
  private transferState = inject(TransferState);
  private http = inject(HttpClient);
  private platformId = inject(PLATFORM_ID);
  userData: any;

  ngOnInit() {
    // Check if data is already in transfer state
    const cachedData = this.transferState.get(DATA_KEY, null);
    
    if (cachedData) {
      // Use cached data from server
      this.userData = cachedData;
    } else {
      // Fetch data (happens on server)
      this.http.get('/api/user').subscribe(data => {
        this.userData = data;
        
        // Store in transfer state if on server
        if (isPlatformServer(this.platformId)) {
          this.transferState.set(DATA_KEY, data);
        }
      });
    }
  }
}
```

<Note>
  When using `provideHttpClient()` with `withFetch()`, transfer state is automatically handled for HTTP requests.
</Note>

## Best Practices

### 1. Handle Platform Differences

Use platform checks for code that should only run on server or browser:

```typescript theme={null}
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
import { PLATFORM_ID, inject } from '@angular/core';

export class MyComponent {
  private platformId = inject(PLATFORM_ID);

  ngOnInit() {
    if (isPlatformBrowser(this.platformId)) {
      // Browser-only code (e.g., accessing window, localStorage)
      console.log('Running in browser');
    }
    
    if (isPlatformServer(this.platformId)) {
      // Server-only code
      console.log('Running on server');
    }
  }
}
```

### 2. Avoid Browser-Only APIs

Avoid direct references to browser globals like `window`, `document`, `localStorage`:

```typescript theme={null}
import { DOCUMENT } from '@angular/common';
import { inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

export class SafeComponent {
  private document = inject(DOCUMENT);
  private platformId = inject(PLATFORM_ID);

  getWindowWidth(): number {
    if (isPlatformBrowser(this.platformId)) {
      return window.innerWidth;
    }
    return 0; // Default for server
  }

  setLocalStorage(key: string, value: string): void {
    if (isPlatformBrowser(this.platformId)) {
      localStorage.setItem(key, value);
    }
  }
}
```

### 3. Optimize Performance

Use `BEFORE_APP_SERIALIZED` for cleanup and optimization:

```typescript theme={null}
import { BEFORE_APP_SERIALIZED } from '@angular/platform-server';

export const cleanupProvider = {
  provide: BEFORE_APP_SERIALIZED,
  useFactory: () => {
    return () => {
      // Remove temporary data
      // Close connections
      // Finalize state
    };
  },
  multi: true
};
```

### 4. Handle Absolute URLs

Ensure URLs are absolute when rendering on the server:

```typescript theme={null}
import { Injectable, inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Injectable({ providedIn: 'root' })
export class UrlService {
  private document = inject(DOCUMENT);

  getAbsoluteUrl(path: string): string {
    const baseUrl = this.document.location.origin;
    return `${baseUrl}${path}`;
  }
}
```

## Related APIs

<CardGroup cols={2}>
  <Card title="@angular/platform-browser" icon="browser" href="/api/platform/browser">
    Browser platform APIs and client hydration
  </Card>

  <Card title="TransferState" icon="exchange-alt" href="/api/core/overview">
    Share data between server and client
  </Card>

  <Card title="HttpClient" icon="network-wired" href="/api/http/httpclient">
    HTTP requests with SSR support
  </Card>

  <Card title="Router" icon="route" href="/api/router/router-class">
    Routing in SSR applications
  </Card>
</CardGroup>
