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

# Server-Side Rendering (SSR)

export const VueSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return "Vue";
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || "Vue";
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (code !== "Vue") {
    return null;
  }
  return children;
};

export const SvelteSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return null;
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || null;
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (!code?.includes("Svelte")) {
    return null;
  }
  return children;
};

export const ReactSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return null;
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || null;
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (code !== "React") {
    return null;
  }
  return children;
};

export const ClientSpecific = ({children}) => {
  const [nada, setNada] = useState();
  return children;
};

<Warning>You are viewing the documentation for Inertia.js v2. Inertia.js v3 has been released and is now the default version. Please visit the [upgrade guide](/v3/getting-started/upgrade-guide) to get started.</Warning>

Server-side rendering pre-renders your JavaScript pages on the server, allowing your visitors to receive fully rendered HTML when they visit your application. Since fully rendered HTML is served by your application, it's also easier for search engines to index your site.

Server-side rendering uses Node.js to render your pages in a background process; therefore, Node must be available on your server for server-side rendering to function properly.

## Laravel Starter Kits

If you are using [Laravel Starter Kits](https://laravel.com/docs/starter-kits), Inertia SSR is [supported](https://laravel.com/docs/starter-kits#inertia-ssr) through a build command:

```bash theme={null}
npm run build:ssr
```

## Add Server Entry-Point

To manually configure SSR without a Laravel starter kit, first create an SSR entry point file within your Laravel project.

<CodeGroup>
  ```bash Vue icon="vuejs" theme={null}
  touch resources/js/ssr.js
  ```

  ```bash React icon="react" theme={null}
  touch resources/js/ssr.jsx
  ```

  ```bash Svelte icon="s" theme={null}
  touch resources/js/ssr.js
  ```
</CodeGroup>

This file is going to look very similar to your app entry point, except it's not going to run in the browser, but rather in Node.js. Here's a complete example.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { createInertiaApp } from '@inertiajs/vue3'
  import createServer from '@inertiajs/vue3/server'
  import { renderToString } from 'vue/server-renderer'
  import { createSSRApp, h } from 'vue'

  createServer(page =>
      createInertiaApp({
          page,
          render: renderToString,
          resolve: name => {
              const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
              return pages[`./Pages/${name}.vue`]
          },
          setup({ App, props, plugin }) {
              return createSSRApp({
                  render: () => h(App, props),
              }).use(plugin)
          },
      }),
  )
  ```

  ```jsx React icon="react" theme={null}
  import { createInertiaApp } from '@inertiajs/react'
  import createServer from '@inertiajs/react/server'
  import ReactDOMServer from 'react-dom/server'

  createServer(page =>
      createInertiaApp({
          page,
          render: ReactDOMServer.renderToString,
          resolve: name => {
              const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true })
              return pages[`./Pages/${name}.jsx`]
          },
          setup: ({ App, props }) => <App {...props} />,
      }),
  )
  ```

  ```js Svelte icon="s" theme={null}
  import { createInertiaApp } from '@inertiajs/svelte'
  import createServer from '@inertiajs/svelte/server'
  import { render } from 'svelte/server'

  createServer(page =>
      createInertiaApp({
          page,
          resolve: name => {
              const pages = import.meta.glob('./Pages/**/*.svelte', { eager: true })
              return pages[`./Pages/${name}.svelte`]
          },
          setup({ App, props }) {
              return render(App, { props })
          },
      }),
  )
  ```
</CodeGroup>

When creating this file, be sure to add anything that's missing from your `app.js` file that makes sense to run in SSR mode, such as plugins or custom mixins.

### Clustering

By default, the SSR server will run on a single thread. Clustering starts multiple Node servers on the same port, requests are then handled by each thread in a round-robin way.

You may enable clustering by passing a second argument of options to `createServer`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { createInertiaApp } from '@inertiajs/vue3'
  import createServer from '@inertiajs/vue3/server'
  import { renderToString } from 'vue/server-renderer'
  import { createSSRApp, h } from 'vue'

  createServer(page =>
      createInertiaApp({
          // ...
      }),
      { cluster: true },
  )
  ```

  ```jsx React icon="react" theme={null}
  import { createInertiaApp } from '@inertiajs/react'
  import createServer from '@inertiajs/react/server'
  import ReactDOMServer from 'react-dom/server'

  createServer(page =>
      createInertiaApp({
          // ...
      }),
      { cluster: true },
  )
  ```

  ```js Svelte icon="s" theme={null}
  import { createInertiaApp } from '@inertiajs/svelte'
  import createServer from '@inertiajs/svelte/server'
  import { render } from 'svelte/server'

  createServer(page =>
      createInertiaApp({
          // ...
      }),
      { cluster: true },
  )
  ```
</CodeGroup>

## Client-Side Hydration

<ClientSpecific>
  Since your website will be server-side rendered, you should also update your `app.js` file to use hydration instead of normal rendering. This allows <VueSpecific>Vue</VueSpecific><ReactSpecific>React</ReactSpecific><SvelteSpecific>Svelte</SvelteSpecific> to pick up the server-rendered HTML and make it interactive without re-rendering it.
</ClientSpecific>

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { createApp, h } from 'vue' // [!code --]
  import { createSSRApp, h } from 'vue' // [!code ++]
  import { createInertiaApp } from '@inertiajs/vue3'

  createInertiaApp({
      resolve: name => {
          const pages = import.meta.glob('./Pages/**/*.vue', { eager: true })
          return pages[`./Pages/${name}.vue`]
      },
      setup({ el, App, props, plugin }) {
      createApp({ render: () => h(App, props) }) // [!code --]
      createSSRApp({ render: () => h(App, props) }) // [!code ++]
          .use(plugin)
          .mount(el)
      },
  })
  ```

  ```js React icon="react" theme={null}
  import { createInertiaApp } from '@inertiajs/react'
  import { createRoot } from 'react-dom/client' // [!code --]
  import { hydrateRoot } from 'react-dom/client' // [!code ++]

  createInertiaApp({
      resolve: name => {
          const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true })
          return pages[`./Pages/${name}.jsx`]
      },
      setup({ el, App, props }) {
          createRoot(el).render(<App {...props} />) // [!code --]
          hydrateRoot(el, <App {...props} />) // [!code ++]
      },
  })
  ```

  ```js Svelte icon="s" theme={null}
  import { createInertiaApp } from '@inertiajs/svelte'
  import { mount } from 'svelte' // [!code --]
  import { hydrate, mount } from 'svelte' // [!code ++]

  createInertiaApp({
      resolve: name => {
          const pages = import.meta.glob('./Pages/**/*.svelte', { eager: true })
          return pages[`./Pages/${name}.svelte`]
      },
      setup({ el, App, props }) {
          mount(App, { target: el, props }) // [!code --]
          if (el.dataset.serverRendered === 'true') { // [!code ++:5]
              hydrate(App, { target: el, props })
          } else {
              mount(App, { target: el, props })
          }
      },
  })
  ```
</CodeGroup>

## Setup Vite

Next, update your Vite configuration to build the new SSR entry point by adding a `ssr` property to Laravel's Vite plugin configuration in your `vite.config.js` file.

<CodeGroup>
  ```js Vue icon="vuejs" vite.config.js theme={null}
  export default defineConfig({
      plugins: [
          laravel({
              input: ['resources/js/app.js'],
              ssr: 'resources/js/ssr.js', // [!code ++]
              refresh: true,
          }),
          // ...
      ],
  })
  ```

  ```js React icon="react" vite.config.js theme={null}
  export default defineConfig({
      plugins: [
          laravel({
              input: ['resources/js/app.jsx'],
              ssr: 'resources/js/ssr.jsx', // [!code ++]
              refresh: true,
          }),
          // ...
      ],
  })
  ```

  ```js Svelte icon="s" vite.config.js theme={null}
  export default defineConfig({
      plugins: [
          laravel({
              input: ['resources/js/app.js'],
              ssr: 'resources/js/ssr.js', // [!code ++]
              refresh: true,
          }),
          // ...
      ],
  })
  ```
</CodeGroup>

## Update NPM Script

Next, update the `build` script in your `package.json` file to also build your SSR entry point.

```json package.json theme={null}
"scripts": {
    "dev": "vite",
    "build": "vite build" // [!code --]
    "build": "vite build && vite build --ssr" // [!code ++]
},
```

Now you can build both your client-side and server-side bundles.

```bash theme={null}
npm run build
```

## Running the SSR Server

Now that you have built both your client-side and server-side bundles, you should be able run the Node-based Inertia SSR server using the following command.

```bash theme={null}
php artisan inertia:start-ssr
```

You may use the `--runtime` option to specify which runtime you want to use. This allows you to switch from the default Node.js runtime to Bun.

```bash theme={null}
php artisan inertia:start-ssr --runtime=bun
```

With the server running, you should be able to access your app within the browser with server-side rendering enabled. In fact, you should be able to disable JavaScript entirely and still navigate around your application.

## Disabling SSR

Sometimes you may wish to disable server-side rendering for certain pages or routes in your application. You may do so by setting the `inertia.ssr.enabled` configuration value to `false` for the current request, typically in a service provider or middleware.

```php theme={null}
if (request()->is('admin/*')) {
    config(['inertia.ssr.enabled' => false]);
}
```

## Deployment

When deploying your SSR enabled app to production, you'll need to build both the client-side (`app.js`) and server-side bundles (`ssr.js`), and then run the SSR server as a background process, typically using a process monitoring tool such as Supervisor.

```bash theme={null}
php artisan inertia:start-ssr
```

To stop the SSR server, for instance when you deploy a new version of your website, you may utilize the `inertia:stop-ssr` Artisan command. Your process monitor (such as Supervisor) should be responsible for automatically restarting the SSR server after it has stopped.

```bash theme={null}
php artisan inertia:stop-ssr
```

You may use the `inertia:check-ssr` Artisan command to verify that the SSR server is running. This can be helpful after deployment and works well as a Docker health check to ensure the server is responding as expected.

```bash theme={null}
php artisan inertia:check-ssr
```

By default, a check is performed to ensure the server-side bundle exists before dispatching a request to the SSR server. In some cases, such as when your app runs on multiple servers or is containerized, the web server may not have access to the SSR bundle. To disable this check, you may set the `inertia.ssr.ensure_bundle_exists` configuration value to `false`.

### Laravel Cloud

To run the SSR server on Laravel Cloud, you may use Cloud's [native support for Inertia SSR](https://cloud.laravel.com/docs/compute#inertia-ssr).

### Laravel Forge

To run the SSR server on Forge, you may enable it via the [Inertia SSR toggle](https://forge.laravel.com/docs/sites/laravel#inertia-server-side-rendering-ssr) in your site's application panel. Forge will create the required daemon and, optionally, update your deploy script to restart the SSR server on each deployment.
