> ## 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 Vue3Specific = ({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 !== "Vue 3") {
    return null;
  }
  return children;
};

export const Vue2Specific = ({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 !== "Vue 2") {
    return null;
  }
  return children;
};

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>Đây là tài liệu Inertia.js v1, phiên bản không còn được duy trì tích cực. Vui lòng tham khảo [tài liệu v3](/v3/getting-started/index).</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 Breeze hoặc Jetstream](https://laravel.com/docs/starter-kits), bạn có thể cài scaffold của starter kit với hỗ trợ Inertia SSR được cấu hình sẵn bằng flag `--ssr`.

```bash theme={null}
php artisan breeze:install react --ssr
php artisan breeze:install vue --ssr
```

## Cài đặt dependency

Nếu không dùng Laravel starter kit và muốn cấu hình SSR thủ công, trước tiên chúng ta sẽ cài các dependency bổ sung cần cho server-side rendering. Bước này chỉ cần thiết với adapter Vue, vì vậy bạn có thể bỏ qua nếu dùng React hoặc Svelte.

<CodeGroup>
  ```bash Vue 2 theme={null}
  npm install vue-server-renderer
  ```

  ```bash Vue 3 theme={null}
  npm install @vue/server-renderer
  ```

  ```js React icon="react" theme={null}
  // No additional dependencies required
  ```

  ```js Svelte icon="s" theme={null}
  // No additional dependencies required
  ```
</CodeGroup>

Sau đó, hãy đảm bảo đã cài phiên bản mới nhất của adapter Inertia dành cho Laravel.

```bash theme={null}
composer require inertiajs/inertia-laravel
```

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

Tiếp theo, chúng ta sẽ tạo file `resources/js/ssr.js` trong dự án Laravel để làm entry point cho SSR.

```bash theme={null}
touch resources/js/ssr.js
```

File này sẽ rất giống `resources/js/app.js`, ngoại trừ việc nó không chạy trong trình duyệt mà chạy trong Node.js. Sau đây là ví dụ hoàn chỉnh.

<CodeGroup>
  ```js Vue 2 icon="vuejs" theme={null}
  import { createInertiaApp } from '@inertiajs/vue2'
  import createServer from '@inertiajs/vue2/server'
  import Vue from 'vue'
  import { createRenderer } from 'vue-server-renderer'

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

  ```js Vue 3 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'

  createServer(page =>
      createInertiaApp({
          page,
          resolve: name => {
              const pages = import.meta.glob('./Pages/**/*.svelte', { eager: true })
              return pages[`./Pages/${name}.svelte`]
          },
      }),
  )
  ```
</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.

## Thiết lập Vite

Tiếp theo, chúng ta cần cập nhật cấu hình Vite để build file `ssr.js` mới. Có thể thực hiện bằng cách thêm thuộc tính `ssr` vào cấu hình Vite plugin của Laravel trong file `vite.config.js`.

```js theme={null}
export default defineConfig({
    plugins: [
        laravel({
            input: ['resources/css/app.css', 'resources/js/app.js'],
            ssr: 'resources/js/ssr.js', // [!code ++]
            refresh: true,
        }),
        // ...
    ],
})
```

## Cập nhật script Npm

Tiếp theo, hãy cập nhật script `build` trong file `package.json` để build cả file `ssr.js` mới.

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

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.

## Hydration phía client

<ClientSpecific>
  Vì website hiện được render phía máy chủ, bạn có thể yêu cầu <VueSpecific>Vue</VueSpecific><ReactSpecific>React</ReactSpecific><SvelteSpecific>Svelte</SvelteSpecific> "hydrate" phần markup tĩnh để biến nó thành giao diện tương tác thay vì render lại toàn bộ HTML vừa được tạo.
</ClientSpecific>

<Vue2Specific>Inertia tự động bật hydration phía client trong ứng dụng Vue 2 nên không cần thay đổi gì.</Vue2Specific>

<Vue3Specific>Để bật hydration phía client trong ứng dụng Vue 3, hãy cập nhật file `app.js` để dùng `createSSRApp` thay cho `createApp`:</Vue3Specific>

<ReactSpecific>Để bật hydration phía client trong ứng dụng React, hãy cập nhật file `app.js` để dùng `hydrateRoot` thay cho `createRoot`:</ReactSpecific>

<SvelteSpecific>Để bật hydration phía client trong ứng dụng Svelte, hãy đặt compiler option `hydratable` thành `true` trong file `vite.config.js`:</SvelteSpecific>

<CodeGroup>
  ```js Vue 2 icon="vuejs" theme={null}
  // No changes required
  ```

  ```js Vue 3 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 { svelte } from '@sveltejs/vite-plugin-svelte'
  import laravel from 'laravel-vite-plugin'
  import { defineConfig } from 'vite'

  export default defineConfig({
      plugins: [
          laravel.default({
              input: ['resources/css/app.css', 'resources/js/app.js'],
              ssr: 'resources/js/ssr.js',
              refresh: true,
          }),
          svelte(), // [!code --]
          svelte({ // [!code ++:5]
              compilerOptions: {
                  hydratable: true,
              },
          }),
      ],
  })
  ```
</CodeGroup>

<SvelteSpecific>
  Bạn cũng cần bật hydration trong file `app.js`:

  ```js theme={null}
  import { createInertiaApp } from '@inertiajs/svelte'

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

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

## Laravel Forge

Để chạy SSR server trên Forge, bạn nên tạo một daemon mới chạy `php artisan inertia:start-ssr` từ thư mục gốc ứng dụng. Hoặc bạn có thể dùng tích hợp Inertia có sẵn trong dashboard quản lý ứng dụng Forge.

Sau đó, mỗi lần triển khai ứng dụng, bạn có thể tự động restart SSR server bằng command `php artisan inertia:stop-ssr`. Command này dừng SSR server hiện tại, buộc công cụ giám sát tiến trình khởi động một server mới.

## Heroku

Để chạy SSR server trên Heroku, hãy cập nhật cấu hình `web` trong `Procfile` để chạy SSR server trước khi khởi động web server.

```bash theme={null}
web: php artisan inertia:start-ssr & vendor/bin/heroku-php-apache2 public/
```

Lưu ý, bạn phải cài buildpack `heroku/nodejs` bên cạnh `heroku/php` để SSR server có thể chạy.

***

## 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 v1 chính thức](https://inertiajs.com/docs/v1/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.
