Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/src/pages/en/(pages)/framework/http.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,51 @@ export default function MyComponent() {
};
```

The `redirect()` function accepts an optional third argument `kind` that controls how the redirect is performed on the client. The available kinds are:

| Kind | Description |
| --- | --- |
| `"navigate"` | **(default)** Performs an RSC navigation using `replaceState`. The browser URL changes without adding a history entry. |
| `"push"` | Performs an RSC navigation using `pushState`. The browser URL changes and a new history entry is added, so the user can navigate back. |
| `"location"` | Forces a full browser navigation via `location.href`. Useful for redirecting to external URLs or when a full page reload is needed. |
| `"error"` | Throws the redirect error on the client instead of navigating. This allows custom handling via `try`/`catch` in server action calls. |

```jsx
import { redirect } from "@lazarv/react-server";

// RSC navigation with pushState (adds history entry)
redirect("/dashboard", 302, "push");

// Full browser navigation
redirect("/oauth/authorize", 302, "location");

// Throw on client for custom handling
redirect("/login", 302, "error");
```

When using the `"error"` kind in a server action, the client can catch the redirect error and handle it:

```jsx
"use client";

import { myServerAction } from "./actions";

export function MyComponent() {
const handleClick = async () => {
try {
await myServerAction();
} catch (e) {
if (e?.digest?.startsWith("Location=")) {
const url = e.digest.split("Location=")[1]?.split(";")[0];
console.log(`Redirect to: ${url}`);
}
}
};

return <button onClick={handleClick}>Submit</button>;
}
```

<Link name="rewrite">
## Rewrite
</Link>
Expand Down
13 changes: 13 additions & 0 deletions docs/src/pages/en/(pages)/router/server-routing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ export default function App() {
}
```

You can also specify a `kind` parameter to control how the redirect behaves on the client:

```tsx
import { redirect } from "@lazarv/react-server";

export default function ProtectedPage() {
// Redirect with pushState so the user can navigate back
redirect("/login", 302, "push");
}
```

Available redirect kinds: `"navigate"` (default, replaceState), `"push"` (pushState), `"location"` (full browser navigation), and `"error"` (throw on client for custom handling). See the [HTTP redirect documentation](/framework/http#redirect) for details.

<Link name="rewrites">
## Rewrites
</Link>
Expand Down
45 changes: 45 additions & 0 deletions docs/src/pages/ja/(pages)/framework/http.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,51 @@ export default function MyComponent() {
};
```

`redirect()` 関数は、クライアントでのリダイレクトの動作を制御するオプションの第3引数 `kind` を受け付けます。利用可能な種類は以下の通りです:

| 種類 | 説明 |
| --- | --- |
| `"navigate"` | **(デフォルト)** `replaceState` を使用したRSCナビゲーションを実行します。ブラウザのURLは変更されますが、履歴エントリは追加されません。 |
| `"push"` | `pushState` を使用したRSCナビゲーションを実行します。ブラウザのURLが変更され、新しい履歴エントリが追加されるため、ユーザーは戻るボタンで戻ることができます。 |
| `"location"` | `location.href` を使用した完全なブラウザナビゲーションを強制します。外部URLへのリダイレクトやページの完全なリロードが必要な場合に便利です。 |
| `"error"` | ナビゲーションの代わりにクライアントでリダイレクトエラーをスローします。サーバーアクション呼び出しで `try`/`catch` によるカスタム処理が可能になります。 |

```jsx
import { redirect } from "@lazarv/react-server";

// pushStateを使用したRSCナビゲーション(履歴エントリを追加)
redirect("/dashboard", 302, "push");

// 完全なブラウザナビゲーション
redirect("/oauth/authorize", 302, "location");

// カスタム処理のためにクライアントでスロー
redirect("/login", 302, "error");
```

サーバーアクションで `"error"` 種類を使用する場合、クライアントでリダイレクトエラーをキャッチして処理できます:

```jsx
"use client";

import { myServerAction } from "./actions";

export function MyComponent() {
const handleClick = async () => {
try {
await myServerAction();
} catch (e) {
if (e?.digest?.startsWith("Location=")) {
const url = e.digest.split("Location=")[1]?.split(";")[0];
console.log(`リダイレクト先: ${url}`);
}
}
};

return <button onClick={handleClick}>送信</button>;
}
```

<Link name="rewrite">
## リライト
</Link>
Expand Down
13 changes: 13 additions & 0 deletions docs/src/pages/ja/(pages)/router/server-routing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,19 @@ export default function App() {
}
```

`kind` パラメータを指定して、クライアントでのリダイレクトの動作を制御することもできます:

```tsx
import { redirect } from "@lazarv/react-server";

export default function ProtectedPage() {
// pushStateを使用してリダイレクト(ユーザーが戻れるように)
redirect("/login", 302, "push");
}
```

利用可能なリダイレクト種類:`"navigate"`(デフォルト、replaceState)、`"push"`(pushState)、`"location"`(完全なブラウザナビゲーション)、`"error"`(カスタム処理のためにクライアントでスロー)。詳細は[HTTPリダイレクトのドキュメント](/framework/http#redirect)を参照してください。

<Link name="rewrites">
## リライト
</Link>
Expand Down
74 changes: 74 additions & 0 deletions examples/file-router/components/redirect-kind-buttons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"use client";

import { useState } from "react";

import {
redirectNavigate,
redirectPush,
redirectLocation,
redirectLocationExternal,
redirectError,
} from "../redirect-actions";

export default function RedirectKindButtons() {
const [result, setResult] = useState<string | null>(null);

return (
<div>
<button
data-testid="redirect-navigate"
onClick={async () => {
await redirectNavigate();
}}
>
Navigate (default)
</button>
<br />
<button
data-testid="redirect-push"
onClick={async () => {
await redirectPush();
}}
>
Push (pushState)
</button>
<br />
<button
data-testid="redirect-location"
onClick={async () => {
await redirectLocation();
}}
>
Location (full browser navigation)
</button>
<br />
<button
data-testid="redirect-location-external"
onClick={async () => {
await redirectLocationExternal();
}}
>
Location External
</button>
<br />
<button
data-testid="redirect-error"
onClick={async () => {
try {
await redirectError();
} catch (e: any) {
if (e?.digest?.startsWith("Location=")) {
const url = e.digest.split("Location=")[1]?.split(";")[0];
setResult(`Caught redirect to: ${url}`);
} else {
setResult(`Unexpected error: ${e.message}`);
}
}
}}
>
Error (try/catch)
</button>
{result && <p data-testid="redirect-error-result">{result}</p>}
</div>
);
}
13 changes: 13 additions & 0 deletions examples/file-router/pages/(redirect_extern).middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,17 @@ export default function RedirectMiddleware() {
if (pathname === "/redirect-about") {
redirect("/about");
}
// Redirect kind examples
if (pathname === "/redirect-push") {
redirect("/about", 302, "push");
}
if (pathname === "/redirect-location") {
redirect("/about", 302, "location");
}
if (pathname === "/redirect-location-external") {
redirect("https://react-server.dev", 302, "location");
}
if (pathname === "/redirect-error") {
redirect("/about", 302, "error");
}
}
12 changes: 12 additions & 0 deletions examples/file-router/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ export default function IndexPage() {
<Link to="/redirect-api-external">External with API</Link>
<br />
<Link to="/redirect-about">Internal redirect to existing about page</Link>
<h2>Redirect Kind:</h2>
<Link to="/redirect-kind">Redirect Kind (server actions)</Link>
<br />
<Link to="/redirect-push">Push (middleware)</Link>
<br />
<Link to="/redirect-location">Location (middleware)</Link>
<br />
<Link to="/redirect-location-external">
Location External (middleware)
</Link>
<br />
<Link to="/redirect-error">Error (middleware)</Link>
<h2>Error:</h2>
<Link to="/middleware-error">Throw error in middleware</Link>
</div>
Expand Down
11 changes: 11 additions & 0 deletions examples/file-router/pages/redirect-kind/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import RedirectKindButtons from "../../components/redirect-kind-buttons";

export default function RedirectKindPage() {
return (
<div>
<h1>Redirect Kind</h1>
<p>Test different redirect kinds via server actions:</p>
<RedirectKindButtons />
</div>
);
}
23 changes: 23 additions & 0 deletions examples/file-router/redirect-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"use server";

import { redirect } from "@lazarv/react-server";

export async function redirectNavigate() {
redirect("/about", 302, "navigate");
}

export async function redirectPush() {
redirect("/about", 302, "push");
}

export async function redirectLocation() {
redirect("/about", 302, "location");
}

export async function redirectLocationExternal() {
redirect("https://react-server.dev", 302, "location");
}

export async function redirectError() {
redirect("/about", 302, "error");
}
12 changes: 12 additions & 0 deletions examples/file-router/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"strict": true,
"target": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"module": "ESNext",
"moduleResolution": "Bundler"
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["**/*.js", "**/*.mjs", "node_modules"]
}
Loading