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

# Hướng dẫn nâng cấp lên v3.0

Bạn có thể xem tài liệu cũ của Inertia.js v2.0 tại [inertiajs.com/docs/v2](/v2).

## Có gì mới

Inertia.js v3.0 là major release tập trung vào sự đơn giản và trải nghiệm developer. Axios được thay bằng XHR client tích hợp để bundle nhỏ hơn; SSR hoạt động sẵn trong development mà không cần Node.js server riêng; plugin `@inertiajs/vite` mới tự động xử lý page resolution và cấu hình SSR. Release này còn giới thiệu HTTP request độc lập qua hook `useHttp`, optimistic update với rollback tự động, layout props để chia sẻ dữ liệu giữa page/layout và cơ chế xử lý exception tốt hơn.

<Columns cols={2}>
  <Card title="Plugin Vite" href="/v3/installation/client-side-setup#installation" icon="bolt">
    Tự động page resolution, thiết lập SSR và callback setup/resolve tùy chọn.
  </Card>

  <Card title="HTTP request" href="/v3/the-basics/http-requests" icon="globe">
    Gửi HTTP request độc lập bằng hook `useHttp` mà không kích hoạt page visit.
  </Card>

  <Card title="Cập nhật lạc quan (optimistic updates)" href="/v3/the-basics/optimistic-updates" icon="sparkles">
    Áp dụng thay đổi dữ liệu ngay trước khi server phản hồi, tự động rollback nếu thất bại.
  </Card>

  <Card title="Props của layout" href="/v3/the-basics/layouts#layout-props" icon="layer-group">
    Chia sẻ dữ liệu động giữa page và persistent layout bằng hook useLayoutProps.
  </Card>

  <Card title="SSR đơn giản hơn" href="/v3/advanced/server-side-rendering" icon="server">
    SSR tự động hoạt động trong Vite dev mode. Không cần Node.js server riêng.
  </Card>

  <Card title="Xử lý exception" href="/v3/advanced/error-handling#production" icon="shield">
    Render custom Inertia error page trực tiếp từ exception handler, kèm shared data.
  </Card>
</Columns>

Release này cũng bao gồm nhiều cải tiến khác:

* [Instant visits](/v3/the-basics/instant-visits) chuyển sang target component trước khi server phản hồi
* [Generic cho Form component](/v3/advanced/typescript#form-component) để có error và slot prop type-safe
* [Tắt SSR theo route](/v3/advanced/server-side-rendering#disabling-ssr) qua middleware hoặc facade
* [Thông báo lỗi SSR tốt hơn](/v3/advanced/server-side-rendering#error-handling) với component name, URL và hint có thể hành động
* [Hỗ trợ Enum](/v3/the-basics/responses) trong response `Inertia::render()`
* [Page object trong resolve callback](/v3/installation/client-side-setup#manual-setup) để component resolution nhận biết context
* [HTTP interceptor tích hợp](/v3/installation/client-side-setup#interceptors) không cần Axios
* Tùy chọn [default layout](/v3/the-basics/layouts#default-layouts) trong `createInertiaApp`
* Tùy chọn [`preserveErrors`](/v3/data-props/partial-reloads#preserving-errors) để giữ validation error trong partial reload
* [Blade component](/v3/installation/server-side-setup#setup-root-template) thay thế cho directive `@inertia` và `@inertiaHead`, có hỗ trợ SSR head fallback

## Nâng cấp dependency

Để nâng cấp lên Inertia.js v3.0, trước tiên dùng npm để cài client-side adapter bạn muốn:

<CodeGroup>
  ```bash Vue icon="vuejs" theme={null}
  npm install @inertiajs/vue3@^3.0
  ```

  ```bash React icon="react" theme={null}
  npm install @inertiajs/react@^3.0
  ```

  ```bash Svelte icon="s" theme={null}
  npm install @inertiajs/svelte@^3.0
  ```
</CodeGroup>

Bạn cũng có thể cài Vite plugin tùy chọn mới, cung cấp thiết lập SSR đơn giản hơn và shorthand `pages` cho component resolution:

```bash theme={null}
npm install @inertiajs/vite@^3.0
```

Tiếp theo, nâng cấp package `inertiajs/inertia-laravel`:

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

Sau khi nâng cấp, hãy publish lại file cấu hình Inertia vì cấu trúc đã thay đổi trong v3. Bạn nên review config mới và áp dụng lại các tùy chỉnh:

```bash theme={null}
php artisan vendor:publish --provider="Inertia\\ServiceProvider" --force
```

Bạn cũng nên xóa cached view vì output của Blade directive `@inertia` đã thay đổi:

```bash theme={null}
php artisan view:clear
```

## Breaking changes

***

### Yêu cầu

#### PHP 8.2+ và Laravel 11+

Laravel adapter hiện yêu cầu tối thiểu PHP 8.2 và Laravel 11.

#### React 19+

React adapter giờ yêu cầu React 19. React 18 trở xuống không còn được hỗ trợ.

#### Svelte 5+

Svelte adapter giờ yêu cầu Svelte 5. Svelte 4 trở xuống không còn được hỗ trợ. Toàn bộ code Svelte nên được cập nhật sang rune syntax của Svelte 5 (`$props()`, `$state()`, `$effect()`, v.v.).

### Đã loại bỏ Axios

Inertia không còn đi kèm hay yêu cầu Axios. Với phần lớn ứng dụng, không cần thay đổi. XHR client tích hợp cũng hỗ trợ [interceptors](/v3/installation/client-side-setup#interceptors), nên Axios interceptor có thể migrate trực tiếp. Bạn vẫn có thể tiếp tục dùng Axios qua [Axios adapter](/v3/installation/client-side-setup#using-axios), hoặc cung cấp [custom HTTP client](/v3/installation/client-side-setup#custom-http-client) hoàn toàn riêng.

### Đã loại dependency `qs`

Package `qs` được thay bằng implementation query string tích hợp và không còn là dependency của `@inertiajs/core`. Cách Inertia xử lý query string nội bộ vẫn giữ nguyên, nhưng bạn nên cài trực tiếp `qs` nếu ứng dụng import package này.

```bash theme={null}
npm install qs
```

### Đã loại dependency `lodash-es`

Package `lodash-es` được thay bằng `es-toolkit` và không còn là dependency của `@inertiajs/core`. Bạn nên cài trực tiếp `lodash-es` nếu ứng dụng import package này.

```bash theme={null}
npm install lodash-es
```

### Đổi tên Event

Hai global event được đổi tên để rõ nghĩa hơn:

| Tên v2      | Tên v3          | Document Event          |
| ----------- | --------------- | ----------------------- |
| `invalid`   | `httpException` | `inertia:httpException` |
| `exception` | `networkError`  | `inertia:networkError`  |

Global event listener nên được cập nhật tương ứng:

```js theme={null}
// Before (v2)
router.on('invalid', (event) => { ... })
router.on('exception', (event) => { ... })

// After (v3)
router.on('httpException', (event) => { ... })
router.on('networkError', (event) => { ... })
```

Bạn cũng có thể xử lý các event này theo từng visit bằng callback `onHttpException` và `onNetworkError` mới:

```js theme={null}
router.post('/users', data, {
    onHttpException: (response) => { ... },
    onNetworkError: (error) => { ... },
})
```

Trả `false` từ callback `onHttpException` hoặc gọi `event.preventDefault()` trên global event `httpException` sẽ ngăn Inertia điều hướng tới error page. Điều này cho phép tự xử lý HTTP exception (response 4xx/5xx) mà không rời page hiện tại.

```js theme={null}
router.post('/users', data, {
    onHttpException: (response) => {
        // Handle the error without navigating
        return false
    },
})
```

### `router.cancel()` đã được thay thế

Phương thức `router.cancel()` được thay bằng `router.cancelAll()`. Trong v2, `cancel()` chỉ hủy synchronous request. `cancelAll()` mới mặc định hủy toàn bộ synchronous, asynchronous và prefetch request. Bạn có thể truyền option để giới hạn request type bị hủy.

```js theme={null}
// Before (v2) — only canceled sync requests
router.cancel()

// After (v3) — cancels all request types
router.cancelAll()

// To match v2 behavior (sync only)...
router.cancelAll({ async: false, prefetch: false })
```

Xem tài liệu [visit cancellation](/v3/the-basics/manual-visits#visit-cancellation) để biết thêm.

### Đã loại Future Options

Namespace cấu hình `future` đã bị loại. Cả bốn future option từ v2 giờ luôn bật và không còn cấu hình được:

* `future.preserveEqualProps`
* `future.useDataInertiaHeadAttribute`
* `future.useDialogForErrorModal`
* `future.useScriptElementForInitialPage`

```js theme={null}
// Before (v2)
createInertiaApp({
    defaults: {
        future: {
            preserveEqualProps: true,
            useDataInertiaHeadAttribute: true,
            useDialogForErrorModal: true,
            useScriptElementForInitialPage: true,
        },
    },
})

// After (v3) - just remove the `future` block
createInertiaApp({
    // ...
})
```

Initial page data giờ luôn được truyền qua phần tử `<script type="application/json">`. Cách dùng thuộc tính `data-page` cũ không còn được hỗ trợ.

### Thuộc tính Head Element

Thuộc tính `inertia` dùng trên element trong section `<head>` của root Blade template đã đổi tên thành `data-inertia`. Bạn nên cập nhật các head element đang dùng thuộc tính này:

```html theme={null}
<!-- Before (v2) -->
<title inertia>My Website</title>

<!-- After (v3) -->
<title data-inertia>My Website</title>
```

### Đã loại Progress Indicator Exports

Các named export `hideProgress()` và `revealProgress()` đã bị loại. Nếu cần, hãy dùng trực tiếp object `progress`:

```js theme={null}
import { progress } from '@inertiajs/vue3'

progress.hide()
progress.reveal()
```

### Hành vi Deferred Component (React)

Component `<Deferred>` của React không còn reset để hiển thị fallback trong partial reload. Trước đây fallback xuất hiện mỗi lần partial reload được kích hoạt. Giờ nội dung hiện có vẫn hiển thị trong khi dữ liệu mới tải, nhất quán với Vue và Svelte.

Slot prop `reloading` mới có trên mọi adapter, cho phép hiển thị loading indicator trong partial reload mà vẫn giữ nội dung hiện có. Xem tài liệu [deferred props](/v3/data-props/deferred-props#reloading) để biết chi tiết.

### Thời điểm reset trạng thái xử lý Form

Helper `useForm` giờ chỉ reset state `processing` và `progress` trong callback `onFinish`, thay vì ngay khi nhận response. Điều này đảm bảo processing state vẫn là `true` cho tới khi visit hoàn tất hoàn toàn.

### Đã loại LazyProp

Phương thức `Inertia::lazy()` và class `LazyProp`, đã deprecated trong v2, đã bị loại bỏ. Hãy dùng `Inertia::optional()` thay thế; phương thức này cung cấp cùng chức năng:

```php theme={null}
// Before (v2)
return Inertia::render('Users/Index', [
    'users' => Inertia::lazy(fn () => User::all()),
]);

// After (v3)
return Inertia::render('Users/Index', [
    'users' => Inertia::optional(fn () => User::all()),
]);
```

### Tái cấu trúc file Config

File cấu hình Laravel đã được tái cấu trúc. Các setting liên quan page giờ nằm dưới `pages`, còn section `testing` được đơn giản hóa:

```php theme={null}
// Before (v2) - config/inertia.php
'testing' => [
    'ensure_pages_exist' => true,
    'page_paths' => [resource_path('js/Pages')],
    'page_extensions' => ['js', 'jsx', 'svelte', 'ts', 'tsx', 'vue'],
],

// After (v3) - config/inertia.php
'pages' => [
    'ensure_pages_exist' => false,
    'paths' => [resource_path('js/Pages')],
    'extensions' => ['js', 'jsx', 'svelte', 'ts', 'tsx', 'vue'],
],

'testing' => [
    'ensure_pages_exist' => true,
],
```

File config mới lẽ ra đã được publish lại trong bước [nâng cấp dependency](#upgrade-dependencies) ở trên.

### Đã loại các Testing Concerns

Các trait deprecated `Inertia\Testing\Concerns\Has`, `Inertia\Testing\Concerns\Matching` và `Inertia\Testing\Concerns\Debugging` đã bị loại. Các trait này deprecated từ v1 và được thay bằng class `AssertableInertia`. Không cần hành động trừ khi ứng dụng tham chiếu trực tiếp các trait này.

### React Arrow Function Component dùng làm Layout

Implementation layout mới trong v3 không còn hỗ trợ arrow function component gán trực tiếp vào `.layout`. Inertia không thể phân biệt chúng một cách đáng tin cậy với render function tại runtime. Bọc component trong mảng sẽ giải quyết vấn đề:

```jsx theme={null}
const Layout = ({ children }) => <main>{children}</main>

// ❌ does not work — arrow function component assigned directly
Dashboard.layout = Layout

// ✅ wrap in an array instead
Dashboard.layout = [Layout]
```

Component khai báo bằng function declaration tiếp tục hoạt động mà không cần thay đổi.

## Các thay đổi khác

***

### Blade Components

Inertia giờ cung cấp Blade component `<x-inertia::head>` và `<x-inertia::app>` thay cho directive `@inertiaHead` và `@inertia`. Head component nhận fallback content qua slot và chỉ render khi SSR không hoạt động, giải quyết vấn đề lâu năm về thẻ `<title>` bị trùng trong ứng dụng SSR.

```blade resources/views/app.blade.php theme={null}
<html>
    <head>
        @vite('resources/js/app.js')
        <x-inertia::head>
            <title>{{ config('app.name') }}</title>
        </x-inertia::head>
    </head>
    <body>
        <x-inertia::app />
    </body>
</html>
```

Các directive hiện có vẫn tiếp tục hoạt động và không cần thay đổi.

### SSR trong Development

Khi dùng plugin `@inertiajs/vite` mới, SSR tự động hoạt động trong development chỉ bằng cách chạy `npm run dev`. Bạn không còn cần build SSR bundle bằng `vite build --ssr` hoặc chạy Node.js server riêng bằng `php artisan inertia:start-ssr` trong development. Các command này giờ chỉ cần cho [production deployment](/v3/advanced/server-side-rendering#running-the-ssr-server).

### Middleware Priority

Inertia middleware giờ tự động được đăng ký trong danh sách [middleware priority](https://laravel.com/docs/middleware#sorting-middleware) của Laravel, đảm bảo nó chạy trước middleware như `ThrottleRequests`. Điều này sửa lỗi khiến request `PUT`/`PATCH`/`DELETE` bị rate limit có thể nhận redirect `302` thay vì `303` đúng, làm browser retry HTTP method ban đầu trên redirect target. Không cần hành động bổ sung.

### Nested Prop Types

Các prop type như `Inertia::optional()`, `Inertia::defer()` và `Inertia::merge()` giờ hoạt động bên trong closure và nested array. Inertia resolve chúng ở mọi depth và dùng dot-notation path trong partial reload metadata.

```php theme={null}
return Inertia::render('Dashboard', [
    'auth' => fn () => [
        'user' => Auth::user(),
        'notifications' => Inertia::defer(fn () => Auth::user()->unreadNotifications),
        'invoices' => Inertia::optional(fn () => Auth::user()->invoices),
    ],
]);
```

Ở phía client, các option `only` và `except`, cũng như component `Deferred` và `WhenVisible`, đều hỗ trợ dot notation để target nested prop.

```js theme={null}
router.reload({ only: ['auth.notifications'] })
```

Các class implement interface [`ProvidesInertiaProperties`](/v3/the-basics/responses#providesinertiaproperties-interface) cũng hoạt động ở mọi mức nesting.

```php theme={null}
return Inertia::render('Dashboard', [
    'auth' => [
        new AuthProps,
        'team' => 'Inertia',
    ],
]);
```

### Build Target ES2022

Các package Inertia giờ target ES2022, tăng từ ES2020 trong v2. Bạn có thể dùng Vite plugin [`@vitejs/plugin-legacy`](https://www.npmjs.com/package/@vitejs/plugin-legacy) nếu ứng dụng cần hỗ trợ browser cũ.

### Package chỉ dùng ESM

Toàn bộ package Inertia giờ chỉ phát hành dạng ES Module. Import CommonJS bằng `require()` không còn được hỗ trợ. Bạn nên cập nhật mọi lệnh gọi `require()` sang câu lệnh `import`.

### Thay đổi Page Object

Các property `clearHistory` và `encryptHistory` trong [page object](/v3/core-concepts/the-protocol#the-page-object) giờ là tùy chọn và chỉ có trong response khi là `true`. Trước đây mọi response đều chứa `"clearHistory": false` và `"encryptHistory": false` kể cả khi history không bị xóa hay mã hóa.

***

## 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 v3 chính thức](https://inertiajs.com/docs/v3/getting-started/upgrade-guide). 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.
