@wular/pnext 0.0.6 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/bin/pnext +5 -0
- package/package.json +1 -1
- package/reference/compat.md +86 -21
- package/reference/config.md +40 -93
- package/reference/css.md +40 -35
- package/reference/dev.md +25 -32
- package/reference/env.md +18 -20
- package/reference/getting-started.md +132 -0
- package/reference/metadata.md +26 -41
- package/reference/navigation.md +58 -105
- package/reference/performance.md +21 -142
- package/reference/rendering.md +73 -70
- package/reference/routing.md +87 -97
- package/reference/typegen.md +21 -27
- package/src/api/client-navigation.ts +11 -8
- package/src/api/link.tsx +5 -3
- package/src/cli/analyze.ts +103 -48
- package/src/cli/create.ts +1 -1
- package/src/cli/index.ts +6 -0
- package/src/client/build.ts +10 -2
- package/src/client/entry.ts +13 -9
- package/src/client/router/events.ts +15 -0
- package/src/client/router/runtime.ts +40 -9
- package/src/client/router/types.ts +5 -0
- package/src/config.ts +18 -1
- package/src/dev/server.ts +2 -2
- package/src/render/renderer.ts +11 -0
- package/src/render/static-slots-revive.ts +65 -0
- package/src/render/static-slots.ts +3 -54
- package/src/routing/href.ts +19 -1
- package/src/runtime/loader.ts +3 -1
- package/src/runtime/vendor.ts +5 -4
- package/src/types.ts +5 -1
- package/reference/overview.md +0 -35
package/reference/env.md
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
# Environment Variables
|
|
2
2
|
|
|
3
|
-
pnext loads `.env*` files from the project root,
|
|
3
|
+
pnext loads `.env*` files from the project root, never from `src/`. Variables are server-only unless their name marks them for the browser. Server Components, layouts, route handlers, and the proxy read them from `process.env`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## What reaches the browser
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
2. `.env.local` except when `NODE_ENV` is `test`
|
|
9
|
-
3. `.env.$NODE_ENV`
|
|
10
|
-
4. `.env`
|
|
11
|
-
|
|
12
|
-
If `NODE_ENV` is not set, `pnext dev` uses `development`; other commands use `production`.
|
|
13
|
-
|
|
14
|
-
## Browser Variables
|
|
15
|
-
|
|
16
|
-
Browser bundles inline only:
|
|
7
|
+
Browser bundles inline only these:
|
|
17
8
|
|
|
18
9
|
- `process.env.NODE_ENV`
|
|
19
10
|
- `process.env.NEXT_PUBLIC_*`
|
|
20
11
|
- `process.env.PNEXT_PUBLIC_*`
|
|
21
12
|
|
|
22
|
-
|
|
13
|
+
## Load order
|
|
14
|
+
|
|
15
|
+
Variables already set on `process.env` win. Missing ones are loaded from:
|
|
23
16
|
|
|
24
|
-
|
|
17
|
+
1. `.env.$NODE_ENV.local`
|
|
18
|
+
2. `.env.local`, except when `NODE_ENV` is `test`
|
|
19
|
+
3. `.env.$NODE_ENV`
|
|
20
|
+
4. `.env`
|
|
21
|
+
|
|
22
|
+
If `NODE_ENV` is not set, the dev server uses `development` and every other command uses `production`.
|
|
25
23
|
|
|
26
|
-
|
|
24
|
+
## Variable references
|
|
25
|
+
|
|
26
|
+
A value can reference an earlier variable, with or without braces, and with an optional fallback.
|
|
27
27
|
|
|
28
28
|
```env
|
|
29
29
|
API_HOST=api.example.com
|
|
@@ -31,10 +31,8 @@ NEXT_PUBLIC_API_URL=https://$API_HOST
|
|
|
31
31
|
PORT=${APP_PORT:-3000}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Escape the dollar sign when it should be literal, as `\$10`.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
## PNEXT_COMPAT
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
PRICE=\$10
|
|
40
|
-
```
|
|
38
|
+
Setting it to `next` gives an app with no `pnext.config.ts` the `next/*` module surface and `next.config.js` support. When a config file is present, it forces Next compatibility on and leaves every other compat flag as written. It is read after the `.env*` files load, so a `.env` file can set it. See [Compatibility](./compat.md).
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
pnext is a Preact framework for file-routed apps. It renders on the server by default and ships client JavaScript only where a component opts in.
|
|
4
|
+
|
|
5
|
+
> pnext requires [Bun](https://bun.sh/get) - make sure it is installed first.
|
|
6
|
+
|
|
7
|
+
<!-- tabs:start -->
|
|
8
|
+
|
|
9
|
+
## New app
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bunx @wular/pnext create my-app
|
|
13
|
+
cd my-app
|
|
14
|
+
bun dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The app is running at `http://localhost:3000`. Edit `app/page.tsx`; the browser reloads on save.
|
|
18
|
+
|
|
19
|
+
`app/page.tsx` is a Server Component: it can be async, load data on the server, and ships no client JavaScript. The counter it renders, `app/counter.tsx`, starts with `'use client'`, so it alone hydrates in the browser. That's the model: files name routes, the server renders by default, components opt in to the client.
|
|
20
|
+
|
|
21
|
+
## Coming from Next.js
|
|
22
|
+
|
|
23
|
+
Run in your Next project:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
bunx @wular/pnext migrate
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
It converts the project to pnext with `compat.next` enabled, without editing app source. `--dry-run` previews.
|
|
30
|
+
|
|
31
|
+
With `compat.next`, the `next/*` modules, `next.config.js`, server actions, metadata, and the rest of the App Router behavior work as they do in Next, validated against Next's own test suite. [Compatibility](./compat.md) covers the full surface and where it stops short.
|
|
32
|
+
|
|
33
|
+
<!-- tabs:end -->
|
|
34
|
+
|
|
35
|
+
## Build and deploy
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
pnext build
|
|
39
|
+
pnext start
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`pnext build` makes the production build and `pnext start` serves it. Routes render on the server per request; ones that never read the request are prerendered to static HTML at build time.
|
|
43
|
+
|
|
44
|
+
That deploys anywhere Bun runs. For Vercel, build with `pnext build --adapter vercel` and it writes ready-to-deploy Build Output.
|
|
45
|
+
|
|
46
|
+
## A quick tour
|
|
47
|
+
|
|
48
|
+
### Your first page
|
|
49
|
+
|
|
50
|
+
Routes live in `app/`. A `page.tsx` is a Server Component by default. It runs only on the server, so it can be async and talk to your database, filesystem, or internal services directly. None of that code reaches the browser:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// app/posts/[id]/page.tsx
|
|
54
|
+
import type { PageProps } from '#gen/app/posts/[id]/page'
|
|
55
|
+
|
|
56
|
+
export default async function Page({ params }: PageProps) {
|
|
57
|
+
const { id } = await params
|
|
58
|
+
const post = await db.post.findUnique({ where: { id } })
|
|
59
|
+
return (
|
|
60
|
+
<article>
|
|
61
|
+
<h1>{post.title}</h1>
|
|
62
|
+
<p>{post.body}</p>
|
|
63
|
+
</article>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This page ships **0 KB** of JavaScript. Layouts work the same way: the root `layout.tsx` owns `<html>` and `<body>` and can export `metadata`.
|
|
69
|
+
|
|
70
|
+
### Adding interactivity
|
|
71
|
+
|
|
72
|
+
Mark a component with `"use client"` where you need state, effects, or event handlers. Server Components render it into the page, and only that island hydrates in the browser:
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
// app/counter.tsx
|
|
76
|
+
'use client'
|
|
77
|
+
import { useState } from 'preact/hooks'
|
|
78
|
+
|
|
79
|
+
export function Counter({ initial }: { initial: number }) {
|
|
80
|
+
const [count, setCount] = useState(initial)
|
|
81
|
+
return (
|
|
82
|
+
<button onClick={() => setCount(count + 1)}>Count {count}</button>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Client Components hydrate on Preact for ~7.5 KB of framework, or ~12.5 KB with [`compat.react`](./compat.md) if you want to run React components and libraries unchanged. Offscreen islands can stay out of the initial bundle entirely with `dynamic({ load: 'visible' })`.
|
|
88
|
+
|
|
89
|
+
### Streaming
|
|
90
|
+
|
|
91
|
+
Wrap slow server work in `<Suspense>`. The shell streams immediately and the content follows when it's ready:
|
|
92
|
+
|
|
93
|
+
```tsx
|
|
94
|
+
import { Suspense } from '@wular/pnext'
|
|
95
|
+
|
|
96
|
+
export default function Page() {
|
|
97
|
+
return (
|
|
98
|
+
<Suspense fallback={<p>Loading comments…</p>}>
|
|
99
|
+
<Comments />
|
|
100
|
+
</Suspense>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### APIs and everything else
|
|
106
|
+
|
|
107
|
+
A `route.ts` file is an HTTP handler:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// app/api/users/route.ts
|
|
111
|
+
export async function GET(request: NextRequest) {
|
|
112
|
+
return Response.json(await listUsers())
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- `proxy.ts` runs before route matching.
|
|
117
|
+
- `loading.tsx`, `error.tsx`, and `not-found.tsx` define per-segment fallbacks.
|
|
118
|
+
- `pnext build` makes the production build and `pnext start` serves it. Routes that never read the request are prerendered to static HTML.
|
|
119
|
+
|
|
120
|
+
## References
|
|
121
|
+
|
|
122
|
+
- [Routing](./routing.md)
|
|
123
|
+
- [Navigation](./navigation.md)
|
|
124
|
+
- [Rendering](./rendering.md)
|
|
125
|
+
- [Metadata](./metadata.md)
|
|
126
|
+
- [CSS](./css.md)
|
|
127
|
+
- [Environment Variables](./env.md)
|
|
128
|
+
- [Config](./config.md)
|
|
129
|
+
- [Type Safety](./typegen.md)
|
|
130
|
+
- [Compatibility](./compat.md)
|
|
131
|
+
- [Development](./dev.md)
|
|
132
|
+
- [Performance](./performance.md)
|
package/reference/metadata.md
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
# Metadata
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Titles, descriptions, icons, and social images for a route. Metadata takes no request input in core, so it all resolves during the build, and the server sends the prebuilt head for a static route without recomputing it.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## metadata
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Pages and layouts can export `metadata` as an object:
|
|
7
|
+
Pages and layouts export it as an object. Metadata resolves from the root layout down to the page, and nearer segments override or extend what earlier ones set.
|
|
10
8
|
|
|
11
9
|
```tsx
|
|
12
10
|
import type { Metadata } from '@wular/pnext'
|
|
@@ -17,25 +15,24 @@ export const metadata: Metadata = {
|
|
|
17
15
|
}
|
|
18
16
|
```
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
## Fields
|
|
19
|
+
|
|
20
|
+
pnext follows the Next.js field shapes for `title`, `description`, `metadataBase`, `openGraph`, `twitter`, `robots`, `icons`, `manifest`, `alternates`, `verification`, `appleWebApp`, `appLinks`, and `other`. The [Next.js metadata fields reference](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#metadata-fields) documents the full schema.
|
|
21
|
+
|
|
22
|
+
## metadata()
|
|
23
|
+
|
|
24
|
+
A no-argument function export, for values that come from somewhere else. It runs during the build.
|
|
21
25
|
|
|
22
26
|
```tsx
|
|
23
27
|
export async function metadata() {
|
|
24
28
|
const site = await getSiteMetadata()
|
|
25
|
-
return {
|
|
26
|
-
title: site.title,
|
|
27
|
-
description: site.description,
|
|
28
|
-
}
|
|
29
|
+
return { title: site.title, description: site.description }
|
|
29
30
|
}
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
## viewport
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
## Viewport
|
|
37
|
-
|
|
38
|
-
Pages and layouts can export `viewport` as an object or a build-time function:
|
|
35
|
+
Pages and layouts export it as an object or as a build-time function, the same way.
|
|
39
36
|
|
|
40
37
|
```tsx
|
|
41
38
|
import type { Viewport } from '@wular/pnext'
|
|
@@ -47,40 +44,28 @@ export const viewport: Viewport = {
|
|
|
47
44
|
}
|
|
48
45
|
```
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
## File Conventions
|
|
47
|
+
## Metadata files
|
|
53
48
|
|
|
54
|
-
|
|
49
|
+
Drop these into a route segment and pnext picks them up:
|
|
55
50
|
|
|
56
|
-
- `icon`, `apple-icon`, `opengraph-image`, `twitter-image
|
|
57
|
-
- `sitemap.xml
|
|
58
|
-
- `favicon.ico`, `robots.txt`, `manifest.json`, `manifest.webmanifest
|
|
51
|
+
- `icon`, `apple-icon`, `opengraph-image`, and `twitter-image`, as `.ico`, `.jpg`, `.jpeg`, `.png`, `.svg`, `.gif`, or `.webp`, optionally numbered as `icon2.png`.
|
|
52
|
+
- `sitemap.xml`.
|
|
53
|
+
- `favicon.ico`, `robots.txt`, `manifest.json`, and `manifest.webmanifest`, at the app root only.
|
|
59
54
|
|
|
60
|
-
|
|
55
|
+
The build copies them into the public output. Image and sitemap files apply to their segment and its descendants, while the root-only files are global.
|
|
61
56
|
|
|
62
|
-
##
|
|
57
|
+
## Generated metadata files
|
|
63
58
|
|
|
64
|
-
|
|
59
|
+
Each of the above has a code variant, written in `.tsx`, `.ts`, `.jsx`, `.js`, or `.mjs`: `icon`, `apple-icon`, `opengraph-image`, `twitter-image`, and `sitemap`, plus root-only `robots` and `manifest`.
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
Code metadata route handlers can keep their metadata-route signatures, including `generateImageMetadata()` and `generateSitemaps()`, but core treats their results as build artifacts. Request-time variants that depend on `headers()`, `cookies()`, or dynamic request state belong in `compat.next`.
|
|
61
|
+
These modules run at build time, and their generated links and route outputs are stored in the build output rather than recomputed per request. They can keep their metadata-route signatures, including `generateImageMetadata()` and `generateSitemaps()`, but core treats the results as build artifacts. Use `params()` to choose build-time params, as shown in [Type Safety](./typegen.md).
|
|
69
62
|
|
|
70
63
|
## Precedence
|
|
71
64
|
|
|
72
|
-
File-based metadata
|
|
73
|
-
|
|
74
|
-
The request path determines which segment metadata files apply. Route groups and parallel-route slot directories do not add URL path segments, but their metadata file output names are disambiguated at build time.
|
|
75
|
-
|
|
76
|
-
## Static Params
|
|
65
|
+
File-based metadata outranks page and layout metadata for images and icons, unless the page or layout defines its own image list.
|
|
77
66
|
|
|
78
|
-
|
|
67
|
+
The request path decides which segment's metadata files apply. Route groups and slot directories add no URL segment, and their metadata output names are disambiguated during the build.
|
|
79
68
|
|
|
80
|
-
|
|
81
|
-
export function params() {
|
|
82
|
-
return [{ slug: 'intro' }]
|
|
83
|
-
}
|
|
84
|
-
```
|
|
69
|
+
## Request-time metadata
|
|
85
70
|
|
|
86
|
-
`
|
|
71
|
+
Anything that depends on the request needs `compat.next`: `generateMetadata()`, `generateViewport()`, streaming metadata, `headers()`, `cookies()`, and full `next/og` behavior. Under compat, `generateStaticParams()` also serves as the fallback static params provider for a module with no `params()` export. See [Compatibility](./compat.md).
|
package/reference/navigation.md
CHANGED
|
@@ -1,25 +1,10 @@
|
|
|
1
1
|
# Navigation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Typed Hrefs
|
|
6
|
-
|
|
7
|
-
Use `href()` when building URLs outside JSX.
|
|
8
|
-
|
|
9
|
-
```tsx
|
|
10
|
-
import { href } from '@wular/pnext/navigation'
|
|
11
|
-
|
|
12
|
-
const userHref = href('/users/[id]', {
|
|
13
|
-
params: { id: 'ada' },
|
|
14
|
-
search: { tab: 'runs' },
|
|
15
|
-
})
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Dynamic params are checked from generated route types. Routes without params do not take a `params` object.
|
|
3
|
+
Links, prefetching, redirects, and the client router. Server and browser APIs sit in separate entry points, so a server route never pulls in router code meant for the browser.
|
|
19
4
|
|
|
20
5
|
## Link
|
|
21
6
|
|
|
22
|
-
|
|
7
|
+
Renders a normal anchor. The href is a route template, and params are checked against the generated route types, so a wrong name or a missing param fails TypeScript. Use a plain anchor for external URLs and for links you do not want typed.
|
|
23
8
|
|
|
24
9
|
```tsx
|
|
25
10
|
import { Link } from '@wular/pnext/link'
|
|
@@ -37,120 +22,88 @@ export function Nav() {
|
|
|
37
22
|
}
|
|
38
23
|
```
|
|
39
24
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
Use a normal `<a>` for external URLs or intentionally untyped links.
|
|
53
|
-
|
|
54
|
-
## Soft Navigation
|
|
55
|
-
|
|
56
|
-
`<Link>` clicks and `router.push()`/`router.replace()` swap the page in place instead of reloading the document, so shared chunks and CSS are never re-downloaded. Back and forward stay soft and restore scroll. Cross-origin targets, non-HTML responses, and fetch failures fall back to a full page load.
|
|
57
|
-
|
|
58
|
-
`prefetch` on `<Link>` warms the target page and its assets:
|
|
59
|
-
|
|
60
|
-
- `'intent'`: on hover, touch, or focus.
|
|
61
|
-
- `'visible'` (default): when the link enters the viewport.
|
|
62
|
-
- `'load'`: as soon as the link renders.
|
|
63
|
-
- `false`: never; pair with `router.prefetch(href)` for manual control.
|
|
64
|
-
|
|
65
|
-
Prefetch requests ride low network priority, so they never compete with page-critical loading, and concurrency is capped at 4 in flight (12 for hover intent). Prefetched pages expire after 5 minutes and each navigation refetches on the next visit, so pages always reflect fresh server state. Prefetch is a no-op under `pnext dev`.
|
|
25
|
+
| Prop | Value |
|
|
26
|
+
| ------------------------------------- | ------------------------------------------------------------- |
|
|
27
|
+
| `href` | Generated route path. |
|
|
28
|
+
| `params` | Required for dynamic routes. |
|
|
29
|
+
| `search` | Query params. |
|
|
30
|
+
| `hash` | Hash fragment. |
|
|
31
|
+
| `prefetch` | `false`, `'intent'`, `'visible'`, or `'load'`. |
|
|
32
|
+
| `replace` | Marks the navigation as a history replace. |
|
|
33
|
+
| `scroll` | `false` preserves scroll on client navigation. |
|
|
34
|
+
| `onNavigate` | Same-origin client callback that can call `preventDefault()`. |
|
|
35
|
+
| `onPrefetchStart`, `onPrefetchFinish` | Fire when this link's prefetch begins and settles. |
|
|
66
36
|
|
|
67
|
-
##
|
|
37
|
+
## Prefetching
|
|
68
38
|
|
|
69
|
-
|
|
39
|
+
Prefetch warms the target page and its assets. Set the mode per link with the `prefetch` prop, as in `<Link prefetch="intent">`.
|
|
70
40
|
|
|
71
|
-
|
|
41
|
+
- `'visible'` is the default, and fires when the link enters the viewport.
|
|
42
|
+
- `'intent'` fires on hover, touch, or focus.
|
|
43
|
+
- `'load'` fires as soon as the link renders.
|
|
44
|
+
- `false` never prefetches. Pair it with `router.prefetch(href)` for manual control.
|
|
72
45
|
|
|
73
|
-
|
|
74
|
-
import { redirect } from '@wular/pnext/navigation'
|
|
46
|
+
The app-wide default can be set with the `prefetch` field in `pnext.config.ts`, described in [Config](./config.md).
|
|
75
47
|
|
|
76
|
-
|
|
77
|
-
const user = await getUser()
|
|
78
|
-
if (!user) redirect('/login')
|
|
79
|
-
return <Dashboard user={user} />
|
|
80
|
-
}
|
|
81
|
-
```
|
|
48
|
+
Requests use low network priority. At most four run at once, though the hover-intent lane allows up to twelve. The core fallback expiry is five minutes. Prefetch does nothing in development.
|
|
82
49
|
|
|
83
|
-
|
|
50
|
+
## Soft navigation
|
|
84
51
|
|
|
85
|
-
|
|
52
|
+
Link clicks and router pushes swap the page in place instead of reloading the document, so shared chunks and CSS are never re-downloaded. Back and forward stay soft and restore scroll. Cross-origin targets, non-HTML responses, and fetch failures fall back to a full page load.
|
|
86
53
|
|
|
87
|
-
|
|
54
|
+
## Redirects and not found
|
|
88
55
|
|
|
89
56
|
```tsx
|
|
90
|
-
import { href, redirect } from '@wular/pnext/navigation'
|
|
57
|
+
import { href, notFound, redirect } from '@wular/pnext/navigation'
|
|
91
58
|
|
|
59
|
+
redirect('/login')
|
|
60
|
+
redirect('/login', 308)
|
|
92
61
|
redirect(href('/users/[id]', { params: { id: 'ada' } }))
|
|
62
|
+
notFound()
|
|
93
63
|
```
|
|
94
64
|
|
|
95
|
-
|
|
65
|
+
`redirect()` stops server rendering and returns a redirect response. Call it from pages, server components, route handlers, and any server helper used during rendering. Validate targets that come from user input.
|
|
96
66
|
|
|
97
|
-
|
|
67
|
+
The status is 307 unless you pass 303, 307, or 308. `permanentRedirect()` returns 308.
|
|
98
68
|
|
|
99
|
-
|
|
100
|
-
'use client'
|
|
69
|
+
`notFound()` renders the nearest `not-found` fallback with a 404 response.
|
|
101
70
|
|
|
102
|
-
|
|
71
|
+
In the browser, `redirect()` navigates directly. Keep it as the last statement of an event handler, or return it, because browser navigation does not synchronously stop the rest of the handler. Use the router when you want explicit history control.
|
|
103
72
|
|
|
104
|
-
|
|
105
|
-
const router = useRouter()
|
|
106
|
-
const searchParams = useSearchParams()
|
|
73
|
+
## href()
|
|
107
74
|
|
|
108
|
-
|
|
109
|
-
<button
|
|
110
|
-
onClick={() => {
|
|
111
|
-
const search = new URLSearchParams(searchParams.toString())
|
|
112
|
-
search.set('sort', 'asc')
|
|
113
|
-
router.replace('/products', { search, scroll: false })
|
|
114
|
-
}}
|
|
115
|
-
>
|
|
116
|
-
Sort
|
|
117
|
-
</button>
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
```
|
|
75
|
+
Builds a typed URL outside JSX. Routes without params take no params object.
|
|
121
76
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- `useRoute<Route>()`: pathname, route template, params, and search params together.
|
|
129
|
-
- `useLinkStatus()`: pending shape for link feedback.
|
|
77
|
+
```ts
|
|
78
|
+
href('/users/[id]', {
|
|
79
|
+
params: { id: 'ada' },
|
|
80
|
+
search: { tab: 'runs' },
|
|
81
|
+
})
|
|
82
|
+
```
|
|
130
83
|
|
|
131
|
-
|
|
84
|
+
## Client hooks
|
|
132
85
|
|
|
133
|
-
|
|
86
|
+
Exported from `@wular/pnext/navigation/client`, for use in components marked `"use client"`.
|
|
134
87
|
|
|
135
|
-
|
|
88
|
+
| Hook | Returns |
|
|
89
|
+
| -------------------- | ------------------------------------------------------------- |
|
|
90
|
+
| `useRouter()` | `push`, `replace`, `prefetch`, `refresh`, `back`, `forward`. |
|
|
91
|
+
| `usePathname()` | Current browser pathname. |
|
|
92
|
+
| `useSearchParams()` | Read-only URL search params. |
|
|
93
|
+
| `useParams<Route>()` | Current route params from the initial route state. |
|
|
94
|
+
| `useRoute<Route>()` | Pathname, route template, params, and search params together. |
|
|
95
|
+
| `useLinkStatus()` | Currently always `{ pending: false }`. |
|
|
136
96
|
|
|
137
|
-
|
|
138
|
-
import { dynamic } from '@wular/pnext/dynamic'
|
|
139
|
-
|
|
140
|
-
const Chart = dynamic(
|
|
141
|
-
() => import('./chart').then(module => module.Chart),
|
|
142
|
-
{
|
|
143
|
-
load: 'visible',
|
|
144
|
-
},
|
|
145
|
-
)
|
|
146
|
-
```
|
|
97
|
+
Router pushes and replaces are typed like links. They soft-navigate when the path or query changes, and fall back to the History API for same-URL hash updates. Cross-origin targets become a full page load. Refreshing re-fetches the current route in place.
|
|
147
98
|
|
|
148
|
-
|
|
99
|
+
`useSearchParams()` returns a `ReadonlyURLSearchParams`, whose mutation methods throw. Copy it before changing it.
|
|
149
100
|
|
|
150
|
-
```
|
|
151
|
-
const
|
|
101
|
+
```ts
|
|
102
|
+
const search = new URLSearchParams(searchParams.toString())
|
|
103
|
+
search.set('sort', 'asc')
|
|
104
|
+
router.replace('/products', { search, scroll: false })
|
|
152
105
|
```
|
|
153
106
|
|
|
154
|
-
|
|
107
|
+
## Auth interrupts
|
|
155
108
|
|
|
156
|
-
|
|
109
|
+
`forbidden()` and `unauthorized()` throw `PNextForbiddenError` and `PNextUnauthorizedError`, both exported from the server navigation module along with `ReadonlyURLSearchParams`. The functions are core exports, but rendering them as 403 and 401 responses needs the experimental `compat.next` auth interrupts. See [Routing](./routing.md#convention-files).
|