Analog
On this page

Server Side Rendering

Analog supports server-side rendering during development and building for production.

Transforming Packages for SSR Compatibility

Some dependencies may need additional transforms to work for server-side rendering. If you receive an error during SSR in development, one option is to add the package(s) to the ssr.noExternal array in the Vite config.

You can use glob patterns to include sets of packages or libraries. Some examples are listed below.

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
  ssr: {
    noExternal: [
      'apollo-angular', // npm package import
      'apollo-angular/**', // npm package import along with sub-packages
      '@spartan-ng/**', // libs under the npmScope inside an Nx workspace
    ],
  },
  // ...other config
}));

For more information about externals with SSR, check out the Vite documentation.

Hybrid Rendering with Client-Only Routes

SSR is enabled by default. For a hybrid approach, you can specify some routes to only be rendered client-side, and not be server side rendered. This is done through the routeRules in the Nitro config passed to the nitro() plugin by specifying an ssr option.

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
  // ...other config
  plugins: [
    analog({
      prerender: {
        routes: ['/', '/404.html'],
      },
    }),
    angular(),
    nitro({
      routeRules: {
        // All admin URLs are only rendered on the client
        '/admin/**': { ssr: false },

        // Render a 404 page as a fallback page
        '/404.html': { ssr: false },
      },
    }),
  ],
}));

Disabling SSR

You can opt-out of it and generate a client-only build by adding the following option to the analog() plugin in your vite.config.ts:

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
  // ...other config
  plugins: [
    analog({
      ssr: false,
      prerender: {
        routes: [],
      },
    }),
  ],
}));

Custom Entry Files

By default, the server renders src/main.server.ts around the index.html document at the project root. Use entryServer and index to point at different files:

import { resolve } from 'node:path';

export default defineConfig(({ mode }) => ({
  plugins: [
    analog({
      entryServer: resolve(import.meta.dirname, 'src/server/main.server.ts'),
      index: 'app.html',
    }),
  ],
}));

Use an absolute path for entryServer. index is relative to the project root, and must also be the HTML input of the client build (build.rollupOptions.input).

Prerendering routes

With SSR, the "/" route is prerendered by default.

It is a necessary step to return a rendered HTML when the user visits the root of the application. The prerendered routes can be customized, but keep in mind to include the "/" route also.

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
  // ...other config
  plugins: [
    analog({
      prerender: {
        routes: ['/', '/about'],
      },
    }),
  ],
}));

Advanced Code Splitting (Vite 8+ / Rolldown)

When using Vite 8+ with Rolldown as the bundler, you can control how client-side chunks are created by passing codeSplitting through the vite.build.rolldownOptions.output config path. This is useful for further optimizing bundle sizes by grouping vendor or shared modules into separate chunks.

import { defineConfig } from 'vite';
import analog from '@analogjs/platform';

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
  // ...other config
  plugins: [analog()],
  build: {
    rolldownOptions: {
      output: {
        codeSplitting: {
          minSize: 10000,
          groups: [
            {
              name: 'vendor',
              test: /node_modules/,
              priority: 10,
            },
          ],
        },
      },
    },
  },
}));

Code Splitting Options

Option Type Description
groups array Define custom chunk groups
minSize number Minimum chunk size in bytes before splitting
maxSize number Maximum chunk size in bytes

Code Splitting Group Options

Option Type Description
name string | function Chunk name or function returning a chunk name
test RegExp | string | function Pattern or predicate used to match module IDs for the group
priority number Priority when a module matches multiple groups
minSize number Minimum size for this group's chunks
maxSize number Maximum size for this group's chunks
minShareCount number Minimum number of chunks sharing a module before splitting

This option only applies when using Rolldown as the bundler (Vite 8+). It has no effect with Rollup-based builds.

You can opt-out of prerendering altogether by passing an empty array of routes.

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => ({
  // ...other config
  plugins: [
    analog({
      ssr: true,
      prerender: {
        routes: async () => {
          return [];
        },
      },
    }),
  ],
}));