> ## Documentation Index
> Fetch the complete documentation index at: https://inertiajs-vi.tuantq.online/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>Bạn đang xem tài liệu Inertia.js v2. Inertia.js v3 đã được phát hành và hiện là phiên bản mặc định. Hãy xem [hướng dẫn nâng cấp](/v3/getting-started/upgrade-guide) để bắt đầu.</Warning>

Server-side rendering render trước các trang JavaScript trên máy chủ, nhờ đó người truy cập nhận được HTML đã được render đầy đủ khi truy cập ứng dụng. Vì ứng dụng phục vụ HTML hoàn chỉnh, các công cụ tìm kiếm cũng dễ index website hơn.

Server-side rendering sử dụng Node.js để render trang trong một tiến trình nền; vì vậy Node phải có sẵn trên máy chủ để SSR hoạt động đúng.

## Laravel Starter Kit

Nếu đang dùng [Laravel Starter Kits](https://laravel.com/docs/starter-kits), Inertia SSR được [hỗ trợ](https://laravel.com/docs/starter-kits#inertia-ssr) thông qua một build command:

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

## Thêm entry point phía máy chủ

Để cấu hình SSR thủ công mà không dùng Laravel starter kit, trước tiên hãy tạo file entry point SSR trong project Laravel.

<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>

File này sẽ rất giống entry point của ứng dụng, ngoại trừ việc nó không chạy trong trình duyệt mà chạy trong Node.js. Dưới đây là ví dụ hoàn chỉnh.

<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>

Khi tạo file này, hãy nhớ bổ sung mọi phần còn thiếu so với `app.js` nhưng phù hợp để chạy ở chế độ SSR, chẳng hạn plugin hoặc custom mixin.

### Clustering

Mặc định, SSR server chạy trên một thread duy nhất. Clustering khởi động nhiều Node server trên cùng một port, sau đó request được phân phối cho từng thread theo cơ chế round-robin.

Bạn có thể bật clustering bằng cách truyền một đối số options thứ hai vào `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>

## Hydration phía client

<ClientSpecific>
  Vì website được render phía server, bạn cũng nên cập nhật file `app.js` để dùng hydration thay cho render thông thường. Điều này cho phép <VueSpecific>Vue</VueSpecific><ReactSpecific>React</ReactSpecific><SvelteSpecific>Svelte</SvelteSpecific> tiếp quản HTML đã được server render và làm cho giao diện có thể tương tác mà không cần render lại từ đầu.
</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>

## Thiết lập Vite

Tiếp theo, cập nhật cấu hình Vite để build entry point SSR mới bằng cách thêm thuộc tính `ssr` vào cấu hình plugin Vite của Laravel trong file `vite.config.js`.

<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>

## Cập nhật NPM script

Tiếp theo, cập nhật script `build` trong file `package.json` để build cả entry point SSR.

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

Giờ bạn có thể build cả bundle phía client và phía máy chủ.

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

## Chạy SSR server

Sau khi đã build cả bundle phía client và phía máy chủ, bạn có thể chạy Inertia SSR server dựa trên Node bằng command sau.

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

Bạn có thể dùng tùy chọn `--runtime` để chỉ định runtime muốn sử dụng. Điều này cho phép chuyển từ runtime Node.js mặc định sang Bun.

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

Khi server đang chạy, bạn có thể truy cập ứng dụng trong trình duyệt với server-side rendering được bật. Thực tế, bạn có thể tắt hoàn toàn JavaScript mà vẫn điều hướng được trong ứng dụng.

## Tắt SSR

Đôi khi bạn có thể muốn tắt server-side rendering cho một số trang hoặc route nhất định trong ứng dụng. Bạn có thể làm vậy bằng cách đặt giá trị cấu hình `inertia.ssr.enabled` thành `false` cho request hiện tại, thường trong service provider hoặc middleware.

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

## Triển khai

Khi triển khai ứng dụng có SSR lên production, bạn cần build cả bundle phía client (`app.js`) và phía máy chủ (`ssr.js`), sau đó chạy SSR server như một background process, thường thông qua công cụ giám sát tiến trình như Supervisor.

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

Để dừng SSR server, chẳng hạn khi triển khai phiên bản mới của website, bạn có thể dùng Artisan command `inertia:stop-ssr`. Công cụ giám sát tiến trình (như Supervisor) cần chịu trách nhiệm tự động khởi động lại SSR server sau khi nó dừng.

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

Bạn có thể dùng Artisan command `inertia:check-ssr` để xác minh SSR server đang chạy. Điều này hữu ích sau khi deploy và rất phù hợp làm Docker health check để đảm bảo server phản hồi đúng như mong đợi.

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

Mặc định, hệ thống sẽ kiểm tra server-side bundle tồn tại trước khi gửi request tới SSR server. Trong một số trường hợp, chẳng hạn ứng dụng chạy trên nhiều server hoặc trong container, web server có thể không truy cập được SSR bundle. Để tắt kiểm tra này, đặt giá trị cấu hình `inertia.ssr.ensure_bundle_exists` thành `false`.

### Laravel Cloud

Để chạy SSR server trên Laravel Cloud, bạn có thể dùng [hỗ trợ Inertia SSR nguyên bản của Cloud](https://cloud.laravel.com/docs/compute#inertia-ssr).

### Laravel Forge

Để chạy SSR server trên Forge, bạn có thể bật nó bằng [tùy chọn Inertia SSR](https://forge.laravel.com/docs/sites/laravel#inertia-server-side-rendering-ssr) trong panel ứng dụng của site. Forge sẽ tạo daemon cần thiết và, nếu bạn chọn, cập nhật deploy script để khởi động lại SSR server sau mỗi lần deploy.

***

## Tài liệu chính thức

Bài dịch này được đối chiếu từ [tài liệu Inertia.js v2 chính thức](https://inertiajs.com/docs/v2/advanced/server-side-rendering). Nếu có khác biệt do phiên bản hoặc cập nhật mới, hãy ưu tiên tài liệu chính thức làm nguồn tham chiếu.
