@funstack/router 0.0.7 → 0.0.9
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/dist/docs/ApiComponentsPage.tsx +33 -0
- package/dist/docs/ApiHooksPage.tsx +8 -3
- package/dist/docs/ApiTypesPage.tsx +56 -8
- package/dist/docs/ApiUtilitiesPage.tsx +26 -8
- package/dist/docs/ExamplesPage.tsx +445 -0
- package/dist/docs/GettingStartedPage.tsx +10 -33
- package/dist/docs/LearnNavigationApiPage.tsx +1 -4
- package/dist/docs/LearnNestedRoutesPage.tsx +8 -6
- package/dist/docs/LearnRscPage.tsx +77 -94
- package/dist/docs/LearnSsgPage.tsx +133 -0
- package/dist/docs/{LearnSsrPage.tsx → LearnSsrBasicPage.tsx} +44 -21
- package/dist/docs/LearnSsrWithLoadersPage.tsx +141 -0
- package/dist/docs/LearnTransitionsPage.tsx +80 -6
- package/dist/docs/LearnTypeSafetyPage.tsx +28 -22
- package/dist/docs/index.md +5 -2
- package/dist/index.d.mts +56 -25
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +118 -26
- package/dist/index.mjs.map +1 -1
- package/dist/{route-ClVnhrQD.d.mts → route-DRcgs0Pt.d.mts} +61 -6
- package/dist/route-DRcgs0Pt.d.mts.map +1 -0
- package/dist/route-p_gr5yPI.mjs.map +1 -1
- package/dist/server.d.mts +1 -1
- package/package.json +1 -1
- package/dist/route-ClVnhrQD.d.mts.map +0 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { CodeBlock } from "../components/CodeBlock.js";
|
|
2
|
+
|
|
3
|
+
export function LearnSsrWithLoadersPage() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="learn-content">
|
|
6
|
+
<h2>SSR with Loaders</h2>
|
|
7
|
+
|
|
8
|
+
<p className="page-intro">
|
|
9
|
+
When you have a server runtime that can execute loaders at request time,
|
|
10
|
+
set <code>runLoaders: true</code> in the <code>ssr</code> config to
|
|
11
|
+
produce fully rendered HTML — including loader data — on the
|
|
12
|
+
server.
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<section>
|
|
16
|
+
<h3>
|
|
17
|
+
How <code>runLoaders</code> Works
|
|
18
|
+
</h3>
|
|
19
|
+
<p>
|
|
20
|
+
As described in the{" "}
|
|
21
|
+
<a href="/learn/ssr/static-site-generation">Static Site Generation</a>{" "}
|
|
22
|
+
guide, the <code>ssr</code> prop enables path-based route matching
|
|
23
|
+
during SSR. By default, routes with loaders are skipped. Setting{" "}
|
|
24
|
+
<code>runLoaders: true</code> changes this: those routes are matched
|
|
25
|
+
and their loaders execute during SSR.
|
|
26
|
+
</p>
|
|
27
|
+
<CodeBlock language="tsx">{`// Without runLoaders (default): loaders are skipped
|
|
28
|
+
<Router routes={routes} ssr={{ path: "/dashboard" }} />
|
|
29
|
+
|
|
30
|
+
// With runLoaders: loaders execute during SSR
|
|
31
|
+
<Router routes={routes} ssr={{ path: "/dashboard", runLoaders: true }} />`}</CodeBlock>
|
|
32
|
+
<p>
|
|
33
|
+
When loaders run during SSR, their results are passed to components as
|
|
34
|
+
the <code>data</code> prop. The server-rendered HTML includes the
|
|
35
|
+
loader content, so users see the full page immediately.
|
|
36
|
+
</p>
|
|
37
|
+
</section>
|
|
38
|
+
|
|
39
|
+
<section>
|
|
40
|
+
<h3>Example</h3>
|
|
41
|
+
<p>Consider a dashboard route with a loader that fetches user data:</p>
|
|
42
|
+
<CodeBlock language="tsx">{`const routes = [
|
|
43
|
+
route({
|
|
44
|
+
component: AppShell,
|
|
45
|
+
children: [
|
|
46
|
+
route({ path: "/", component: HomePage }),
|
|
47
|
+
route({
|
|
48
|
+
path: "/dashboard",
|
|
49
|
+
component: DashboardPage,
|
|
50
|
+
loader: dashboardLoader,
|
|
51
|
+
}),
|
|
52
|
+
],
|
|
53
|
+
}),
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
// Without runLoaders: DashboardPage is skipped during SSR.
|
|
57
|
+
// Users see only the app shell; dashboard content fills in after hydration.
|
|
58
|
+
<Router routes={routes} ssr={{ path: "/dashboard" }} />
|
|
59
|
+
|
|
60
|
+
// With runLoaders: dashboardLoader executes during SSR.
|
|
61
|
+
// DashboardPage renders with data — users see the full page immediately.
|
|
62
|
+
<Router routes={routes} ssr={{ path: "/dashboard", runLoaders: true }} />`}</CodeBlock>
|
|
63
|
+
</section>
|
|
64
|
+
|
|
65
|
+
<section>
|
|
66
|
+
<h3>Server Setup</h3>
|
|
67
|
+
<p>
|
|
68
|
+
Unlike{" "}
|
|
69
|
+
<a href="/learn/ssr/static-site-generation">static site generation</a>
|
|
70
|
+
, SSR with loaders requires a server runtime that can handle requests
|
|
71
|
+
and render pages dynamically. Your server needs to know the requested
|
|
72
|
+
pathname and pass it to the router:
|
|
73
|
+
</p>
|
|
74
|
+
<CodeBlock language="tsx">{`// App.tsx — receives the pathname from the server
|
|
75
|
+
export default function App({ pathname }: { pathname: string }) {
|
|
76
|
+
return (
|
|
77
|
+
<Router
|
|
78
|
+
routes={routes}
|
|
79
|
+
ssr={{ path: pathname, runLoaders: true }}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
}`}</CodeBlock>
|
|
83
|
+
<p>
|
|
84
|
+
Because loaders run during SSR, they must be able to execute in the
|
|
85
|
+
server environment. If your loaders call browser-only APIs, you may
|
|
86
|
+
need to adjust them or use environment checks.
|
|
87
|
+
</p>
|
|
88
|
+
</section>
|
|
89
|
+
|
|
90
|
+
<section>
|
|
91
|
+
<h3>
|
|
92
|
+
Comparison with <code>ssr</code> (No Loaders)
|
|
93
|
+
</h3>
|
|
94
|
+
<p>
|
|
95
|
+
The choice between <code>ssr</code> alone and{" "}
|
|
96
|
+
<code>ssr + runLoaders</code> depends on your deployment model:
|
|
97
|
+
</p>
|
|
98
|
+
<ul>
|
|
99
|
+
<li>
|
|
100
|
+
<strong>
|
|
101
|
+
<code>{"ssr={{ path }}"}</code>
|
|
102
|
+
</strong>{" "}
|
|
103
|
+
— Ideal for static site generation. Pages without loaders are
|
|
104
|
+
fully pre-rendered. Pages with loaders show the app shell during SSR
|
|
105
|
+
and fill in data after hydration. No server runtime needed.
|
|
106
|
+
</li>
|
|
107
|
+
<li>
|
|
108
|
+
<strong>
|
|
109
|
+
<code>{"ssr={{ path, runLoaders: true }}"}</code>
|
|
110
|
+
</strong>{" "}
|
|
111
|
+
— Ideal for dynamic SSR with a server runtime. All matched
|
|
112
|
+
routes render during SSR, including those with loaders. Users see
|
|
113
|
+
the complete page immediately.
|
|
114
|
+
</li>
|
|
115
|
+
</ul>
|
|
116
|
+
</section>
|
|
117
|
+
|
|
118
|
+
<section>
|
|
119
|
+
<h3>Key Takeaways</h3>
|
|
120
|
+
<ul>
|
|
121
|
+
<li>
|
|
122
|
+
Set <code>runLoaders: true</code> to execute loaders during SSR for
|
|
123
|
+
fully rendered server output
|
|
124
|
+
</li>
|
|
125
|
+
<li>
|
|
126
|
+
Loader results are passed to components as the <code>data</code>{" "}
|
|
127
|
+
prop during SSR, just as they are on the client
|
|
128
|
+
</li>
|
|
129
|
+
<li>
|
|
130
|
+
This mode requires a server runtime that handles requests
|
|
131
|
+
dynamically
|
|
132
|
+
</li>
|
|
133
|
+
<li>
|
|
134
|
+
After hydration, the real URL from the Navigation API takes over and{" "}
|
|
135
|
+
<code>ssr</code> is ignored
|
|
136
|
+
</li>
|
|
137
|
+
</ul>
|
|
138
|
+
</section>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -6,7 +6,7 @@ export function LearnTransitionsPage() {
|
|
|
6
6
|
<h2>Controlling Transitions</h2>
|
|
7
7
|
|
|
8
8
|
<p className="page-intro">
|
|
9
|
-
FUNSTACK Router wraps
|
|
9
|
+
FUNSTACK Router wraps navigations in React's{" "}
|
|
10
10
|
<code>startTransition</code>, which means the old UI may stay visible
|
|
11
11
|
while the new route loads. This page explains how this works and how to
|
|
12
12
|
control it.
|
|
@@ -16,11 +16,11 @@ export function LearnTransitionsPage() {
|
|
|
16
16
|
<h3>Navigations as Transitions</h3>
|
|
17
17
|
<p>
|
|
18
18
|
When the user navigates, the Router updates its location state inside{" "}
|
|
19
|
-
<code>startTransition()</code>. This means React treats
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
<code>startTransition()</code>. This means React treats navigations as
|
|
20
|
+
transitions: if an existing Suspense boundary suspends (e.g., a
|
|
21
|
+
component loading data with <code>use()</code>), React keeps the old
|
|
22
|
+
UI visible instead of immediately showing the fallback. This behavior
|
|
23
|
+
is{" "}
|
|
24
24
|
<a href="https://react.dev/reference/react/useTransition#building-a-suspense-enabled-router">
|
|
25
25
|
what React recommends for Suspense-enabled routers
|
|
26
26
|
</a>
|
|
@@ -141,6 +141,80 @@ function UserDetailPage({
|
|
|
141
141
|
transition behavior is usually the better experience.
|
|
142
142
|
</p>
|
|
143
143
|
</section>
|
|
144
|
+
|
|
145
|
+
<section>
|
|
146
|
+
<h3>Sync State Updates Bypass Transitions</h3>
|
|
147
|
+
<p>
|
|
148
|
+
FUNSTACK Router allows you to save state in a navigation entry, which
|
|
149
|
+
is useful for form state or other UI state that should persist when
|
|
150
|
+
the user navigates back and forth. You can update this state using the{" "}
|
|
151
|
+
<code>setState</code> and <code>resetState</code> functions passed to
|
|
152
|
+
route components. These functions use a replace navigation internally,
|
|
153
|
+
so they trigger a transition.
|
|
154
|
+
</p>
|
|
155
|
+
<p>
|
|
156
|
+
In rare cases, you may want to update navigation state without
|
|
157
|
+
triggering a transition. <code>setStateSync</code> and{" "}
|
|
158
|
+
<code>resetStateSync</code> are designed for this purpose. When you
|
|
159
|
+
call them, the Router updates the current history entry using the
|
|
160
|
+
Navigation API's <code>updateCurrentEntry()</code> method, which does
|
|
161
|
+
not trigger a navigation. The Router detects this and applies the
|
|
162
|
+
update synchronously, outside of <code>startTransition</code>. As a
|
|
163
|
+
result:
|
|
164
|
+
</p>
|
|
165
|
+
<ul>
|
|
166
|
+
<li>
|
|
167
|
+
The update is reflected in the UI immediately — there is no
|
|
168
|
+
pending phase.
|
|
169
|
+
</li>
|
|
170
|
+
<li>
|
|
171
|
+
<code>useIsPending()</code> (and the <code>isPending</code> prop)
|
|
172
|
+
will <strong>not</strong> become <code>true</code>.
|
|
173
|
+
</li>
|
|
174
|
+
<li>
|
|
175
|
+
If the update causes a component to suspend, React will show the
|
|
176
|
+
fallback immediately instead of waiting for the transition to end.
|
|
177
|
+
</li>
|
|
178
|
+
</ul>
|
|
179
|
+
<h4>When to Use Sync State Updates</h4>
|
|
180
|
+
<p>
|
|
181
|
+
When it comes to `setState` vs `setStateSync`, you can think in the
|
|
182
|
+
same way as you would with wrapping state updates in `startTransition`
|
|
183
|
+
or not. A general guideline is to{" "}
|
|
184
|
+
<strong>
|
|
185
|
+
just use <code>setState</code> (with transition)
|
|
186
|
+
</strong>{" "}
|
|
187
|
+
when you don't have a specific reason to avoid it. The exception is
|
|
188
|
+
when the state change <em>already happened</em> on the screen and you
|
|
189
|
+
just want to reflect it in the navigation entry state.
|
|
190
|
+
</p>
|
|
191
|
+
<p>
|
|
192
|
+
A typical example of this is a form where you want to save the current
|
|
193
|
+
input value in the navigation state, so that if the user navigates
|
|
194
|
+
away and then back, their input is preserved. In this case, you would
|
|
195
|
+
call <code>setStateSync</code> in the input's <code>onChange</code>{" "}
|
|
196
|
+
handler, because the state update is already reflected in the input's
|
|
197
|
+
value and you don't want to trigger a transition for this:
|
|
198
|
+
</p>
|
|
199
|
+
<CodeBlock language="tsx">{`function MyForm({ state, setStateSync }: { state: State; setStateSync: (state: State) => void }) {
|
|
200
|
+
const [inputValue, setInputValue] = useState(state.inputValue ?? "");
|
|
201
|
+
|
|
202
|
+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
203
|
+
const newValue = e.target.value;
|
|
204
|
+
setInputValue(newValue);
|
|
205
|
+
setStateSync({ inputValue: newValue }); // Save to navigation state without transition
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
return <input value={inputValue} onChange={handleChange} />;
|
|
209
|
+
}`}</CodeBlock>
|
|
210
|
+
<p>
|
|
211
|
+
In this example, using <code>setState</code> (with transition) would
|
|
212
|
+
cause the UI to enter a pending state on every keystroke, which would
|
|
213
|
+
be a poor user experience. By using <code>setStateSync</code>, the
|
|
214
|
+
navigation state updates seamlessly without triggering transitions or
|
|
215
|
+
pending states.
|
|
216
|
+
</p>
|
|
217
|
+
</section>
|
|
144
218
|
</div>
|
|
145
219
|
);
|
|
146
220
|
}
|
|
@@ -67,7 +67,7 @@ function UserPage({ params }: { params: { userId: string } }) {
|
|
|
67
67
|
For explicit type annotations, use the{" "}
|
|
68
68
|
<code>RouteComponentProps</code> type helper with your params type:
|
|
69
69
|
</p>
|
|
70
|
-
<CodeBlock language="tsx">{`import { route, RouteComponentProps } from "@funstack/router";
|
|
70
|
+
<CodeBlock language="tsx">{`import { route, type RouteComponentProps } from "@funstack/router";
|
|
71
71
|
|
|
72
72
|
// Define component with explicit props type
|
|
73
73
|
function UserPage({ params }: RouteComponentProps<{ userId: string }>) {
|
|
@@ -97,7 +97,7 @@ const userRoute = route({
|
|
|
97
97
|
for routes with loaders.
|
|
98
98
|
</p>
|
|
99
99
|
<CodeBlock language="tsx">{`import { use, Suspense } from "react";
|
|
100
|
-
import { route, RouteComponentPropsWithData } from "@funstack/router";
|
|
100
|
+
import { route, type RouteComponentPropsWithData } from "@funstack/router";
|
|
101
101
|
|
|
102
102
|
interface User {
|
|
103
103
|
id: string;
|
|
@@ -155,7 +155,7 @@ const userRoute = route({
|
|
|
155
155
|
the current page). Use the <code>routeState</code> helper to define
|
|
156
156
|
typed state for your routes.
|
|
157
157
|
</p>
|
|
158
|
-
<CodeBlock language="tsx">{`import {
|
|
158
|
+
<CodeBlock language="tsx">{`import { routeState, RouteComponentProps } from "@funstack/router";
|
|
159
159
|
|
|
160
160
|
// Define the state shape
|
|
161
161
|
interface ProductListState {
|
|
@@ -209,12 +209,10 @@ function ProductListPage({
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
// Use routeState to create a typed route
|
|
212
|
-
const productListRoute = routeState<ProductListState>()(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
})
|
|
217
|
-
);`}</CodeBlock>
|
|
212
|
+
const productListRoute = routeState<ProductListState>()({
|
|
213
|
+
path: "/products",
|
|
214
|
+
component: ProductListPage,
|
|
215
|
+
});`}</CodeBlock>
|
|
218
216
|
<p>
|
|
219
217
|
The <code>routeState</code> helper adds four props to your component:
|
|
220
218
|
</p>
|
|
@@ -225,14 +223,24 @@ const productListRoute = routeState<ProductListState>()(
|
|
|
225
223
|
</li>
|
|
226
224
|
<li>
|
|
227
225
|
<code>setState</code> — Navigate to the same URL with new
|
|
228
|
-
state (creates a new history entry)
|
|
226
|
+
state (creates a new history entry). Goes through a React
|
|
227
|
+
transition, so it may set <code>isPending</code> to{" "}
|
|
228
|
+
<code>true</code>.
|
|
229
229
|
</li>
|
|
230
230
|
<li>
|
|
231
231
|
<code>setStateSync</code> — Update state synchronously without
|
|
232
|
-
creating a new history entry
|
|
232
|
+
creating a new history entry. Bypasses React transitions, so{" "}
|
|
233
|
+
<code>isPending</code> stays <code>false</code>.
|
|
233
234
|
</li>
|
|
234
235
|
<li>
|
|
235
236
|
<code>resetState</code> — Clear the navigation state
|
|
237
|
+
asynchronously via replace navigation. Like <code>setState</code>,
|
|
238
|
+
goes through React transitions.
|
|
239
|
+
</li>
|
|
240
|
+
<li>
|
|
241
|
+
<code>resetStateSync</code> — Clear the navigation state
|
|
242
|
+
synchronously. Like <code>setStateSync</code>, bypasses React
|
|
243
|
+
transitions.
|
|
236
244
|
</li>
|
|
237
245
|
</ul>
|
|
238
246
|
|
|
@@ -302,20 +310,18 @@ function ProductListPage(props: Props) {
|
|
|
302
310
|
}
|
|
303
311
|
|
|
304
312
|
// Route definition with both loader and state
|
|
305
|
-
const productListRoute = routeState<ProductListState>()(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
})
|
|
314
|
-
);`}</CodeBlock>
|
|
313
|
+
const productListRoute = routeState<ProductListState>()({
|
|
314
|
+
path: "/products",
|
|
315
|
+
component: ProductListPage,
|
|
316
|
+
loader: async (): Promise<Product[]> => {
|
|
317
|
+
const response = await fetch("/api/products");
|
|
318
|
+
return response.json();
|
|
319
|
+
},
|
|
320
|
+
});`}</CodeBlock>
|
|
315
321
|
</section>
|
|
316
322
|
|
|
317
323
|
<section>
|
|
318
|
-
<h3>Approach 2: Hooks</h3>
|
|
324
|
+
<h3 id="hooks">Approach 2: Hooks</h3>
|
|
319
325
|
|
|
320
326
|
<h4>When to Use Hooks</h4>
|
|
321
327
|
<p>
|
package/dist/docs/index.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Available Documentation
|
|
4
4
|
|
|
5
|
+
- [Examples](./ExamplesPage.tsx)
|
|
5
6
|
- [Getting Started](./GettingStartedPage.tsx)
|
|
6
7
|
|
|
7
8
|
### API Reference
|
|
@@ -16,6 +17,8 @@
|
|
|
16
17
|
- [Navigation API](./LearnNavigationApiPage.tsx) - FUNSTACK Router is built on the Navigation API , a modern browser API that provides a unified way to handle navigation. This guide explains the key differences from the older History API and the benefits this brings to your application.
|
|
17
18
|
- [Nested Routes](./LearnNestedRoutesPage.tsx) - Nested routes let you build complex page layouts where parts of the UI persist across navigation while other parts change. Think of a dashboard with a sidebar that stays in place while the main content area updates—that's nested routing in action.
|
|
18
19
|
- [React Server Components](./LearnRscPage.tsx) - FUNSTACK Router is designed to work with React Server Components (RSC). The package provides a dedicated server entry point so that route definitions can live in server modules, keeping client bundle sizes small.
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
20
|
+
- [Static Site Generation](./LearnSsgPage.tsx) - When your server or static site generator knows the URL being rendered, you can use the ssr prop to match path-based routes during SSR. This produces richer server-rendered HTML — users see page content immediately instead of just the app shell.
|
|
21
|
+
- [How SSR Works](./LearnSsrBasicPage.tsx) - FUNSTACK Router supports server-side rendering with a two-stage model. During SSR, pathless (layout) routes without loaders render to produce an app shell, while path-based routes and loaders activate only after client hydration.
|
|
22
|
+
- [SSR with Loaders](./LearnSsrWithLoadersPage.tsx) - When you have a server runtime that can execute loaders at request time, set runLoaders: true in the ssr config to produce fully rendered HTML — including loader data — on the server.
|
|
23
|
+
- [Controlling Transitions](./LearnTransitionsPage.tsx) - FUNSTACK Router wraps navigations in React's startTransition, which means the old UI may stay visible while the new route loads. This page explains how this works and how to control it.
|
|
21
24
|
- [Type Safety](./LearnTypeSafetyPage.tsx) - FUNSTACK Router provides first-class TypeScript support, allowing you to access route params, navigation state, and loader data with full type safety. This guide covers two approaches: receiving typed data through component props (recommended) and accessing it through hooks.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as ExtractRouteState, c as PathParams, d as RouteComponentPropsWithData, f as RouteDefinition, h as routeState, i as ExtractRouteParams, l as RouteComponentProps, m as route, n as ExtractRouteData, o as LoaderArgs, p as TypefulOpaqueRouteDefinition, r as ExtractRouteId, s as OpaqueRouteDefinition, t as ActionArgs, u as RouteComponentPropsOf } from "./route-DRcgs0Pt.mjs";
|
|
2
2
|
import { ComponentType, ReactNode } from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
|
@@ -27,15 +27,17 @@ type InternalRouteDefinition = {
|
|
|
27
27
|
* - true (default): Parent does not match if no child matches
|
|
28
28
|
* - false: Parent can match alone if it has a component, outlet will be null
|
|
29
29
|
*/
|
|
30
|
-
requireChildren?: boolean; /**
|
|
31
|
-
|
|
30
|
+
requireChildren?: boolean; /** Action function for handling form submissions (POST navigations) */
|
|
31
|
+
action?: (args: ActionArgs<Record<string, string>>) => unknown; /** Data loader function for this route */
|
|
32
|
+
loader?: (args: LoaderArgs<Record<string, string>, unknown>) => unknown; /** Component to render when this route matches */
|
|
32
33
|
component?: ComponentType<{
|
|
33
34
|
data?: unknown;
|
|
34
35
|
params?: Record<string, string>;
|
|
35
36
|
state?: unknown;
|
|
36
37
|
setState?: (state: unknown | ((prev: unknown) => unknown)) => Promise<void>;
|
|
37
38
|
setStateSync?: (state: unknown | ((prev: unknown) => unknown)) => void;
|
|
38
|
-
resetState?: () => void
|
|
39
|
+
resetState?: () => Promise<void>;
|
|
40
|
+
resetStateSync?: () => void;
|
|
39
41
|
info?: unknown;
|
|
40
42
|
}> | ReactNode;
|
|
41
43
|
};
|
|
@@ -47,18 +49,13 @@ type MatchedRoute = {
|
|
|
47
49
|
params: Record<string, string>; /** The matched pathname segment */
|
|
48
50
|
pathname: string;
|
|
49
51
|
};
|
|
50
|
-
/**
|
|
51
|
-
* A matched route with loader data.
|
|
52
|
-
*/
|
|
53
|
-
type MatchedRouteWithData = MatchedRoute & {
|
|
54
|
-
/** Data returned from the loader (undefined if no loader) */data: unknown | undefined;
|
|
55
|
-
};
|
|
56
52
|
/**
|
|
57
53
|
* Information passed to onNavigate callback.
|
|
58
54
|
*/
|
|
59
55
|
type OnNavigateInfo = {
|
|
60
56
|
/** Array of matched routes, or null if no routes matched */matches: readonly MatchedRoute[] | null; /** Whether the router will intercept this navigation (before user's preventDefault() call) */
|
|
61
|
-
intercepting: boolean;
|
|
57
|
+
intercepting: boolean; /** FormData from the NavigateEvent, or null for non-POST navigations */
|
|
58
|
+
formData: FormData | null;
|
|
62
59
|
};
|
|
63
60
|
/**
|
|
64
61
|
* Options for navigation.
|
|
@@ -93,6 +90,30 @@ type OnNavigateCallback = (event: NavigateEvent, info: OnNavigateInfo) => void;
|
|
|
93
90
|
type FallbackMode = "none" | "static";
|
|
94
91
|
//#endregion
|
|
95
92
|
//#region src/Router.d.ts
|
|
93
|
+
/**
|
|
94
|
+
* SSR configuration for the router.
|
|
95
|
+
*/
|
|
96
|
+
type SSRConfig = {
|
|
97
|
+
/**
|
|
98
|
+
* Pathname to use for route matching during SSR.
|
|
99
|
+
*
|
|
100
|
+
* The router uses this pathname to match path-based routes during SSR.
|
|
101
|
+
* Route params are extracted normally.
|
|
102
|
+
*/
|
|
103
|
+
path: string;
|
|
104
|
+
/**
|
|
105
|
+
* Whether to run loaders during SSR.
|
|
106
|
+
*
|
|
107
|
+
* - When `false` or omitted, routes with loaders are skipped during SSR
|
|
108
|
+
* and the parent route renders as a shell.
|
|
109
|
+
* - When `true`, routes with loaders are matched and their loaders are
|
|
110
|
+
* executed during SSR. The loader results are passed to components as
|
|
111
|
+
* the `data` prop, so server-rendered HTML includes loader content.
|
|
112
|
+
*
|
|
113
|
+
* @default false
|
|
114
|
+
*/
|
|
115
|
+
runLoaders?: boolean;
|
|
116
|
+
};
|
|
96
117
|
type RouterProps = {
|
|
97
118
|
routes: RouteDefinition[];
|
|
98
119
|
/**
|
|
@@ -110,11 +131,33 @@ type RouterProps = {
|
|
|
110
131
|
* - `"static"`: Render matched routes without navigation capabilities (MPA behavior)
|
|
111
132
|
*/
|
|
112
133
|
fallback?: FallbackMode;
|
|
134
|
+
/**
|
|
135
|
+
* SSR configuration for the router.
|
|
136
|
+
*
|
|
137
|
+
* By default (no `ssr` prop), during SSR only pathless routes match.
|
|
138
|
+
* When provided, the router uses the given pathname to match path-based
|
|
139
|
+
* routes during SSR as well.
|
|
140
|
+
*
|
|
141
|
+
* This prop is only used when the location entry is not available (during SSR
|
|
142
|
+
* or hydration). Once the client hydrates, the real URL from the Navigation API
|
|
143
|
+
* takes over.
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```tsx
|
|
147
|
+
* // SSG: match path-based routes, skip loaders
|
|
148
|
+
* <Router routes={routes} ssr={{ path: "/about" }} />
|
|
149
|
+
*
|
|
150
|
+
* // SSR with loaders: match path-based routes including those with loaders
|
|
151
|
+
* <Router routes={routes} ssr={{ path: "/about", runLoaders: true }} />
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
ssr?: SSRConfig;
|
|
113
155
|
};
|
|
114
156
|
declare function Router({
|
|
115
157
|
routes: inputRoutes,
|
|
116
158
|
onNavigate,
|
|
117
|
-
fallback
|
|
159
|
+
fallback,
|
|
160
|
+
ssr
|
|
118
161
|
}: RouterProps): ReactNode;
|
|
119
162
|
//#endregion
|
|
120
163
|
//#region src/Outlet.d.ts
|
|
@@ -260,17 +303,5 @@ declare function useRouteData<T extends TypefulOpaqueRouteDefinition<string, Rec
|
|
|
260
303
|
*/
|
|
261
304
|
declare function useIsPending(): boolean;
|
|
262
305
|
//#endregion
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Represents the current location state.
|
|
266
|
-
* Abstracts NavigationHistoryEntry for static mode compatibility.
|
|
267
|
-
*/
|
|
268
|
-
type LocationEntry = {
|
|
269
|
-
/** The current URL */url: URL; /** Unique key for this entry (used for loader caching) */
|
|
270
|
-
key: string; /** State associated with this entry */
|
|
271
|
-
state: unknown; /** Ephemeral info from current navigation (undefined if not from navigation event) */
|
|
272
|
-
info: unknown;
|
|
273
|
-
};
|
|
274
|
-
//#endregion
|
|
275
|
-
export { type ExtractRouteData, type ExtractRouteId, type ExtractRouteParams, type ExtractRouteState, type FallbackMode, type LoaderArgs, type Location, type LocationEntry, type MatchedRoute, type MatchedRouteWithData, type NavigateOptions, type OnNavigateCallback, type OnNavigateInfo, type OpaqueRouteDefinition, Outlet, type PathParams, type RouteComponentProps, type RouteComponentPropsOf, type RouteComponentPropsWithData, type RouteDefinition, Router, type RouterProps, type TypefulOpaqueRouteDefinition, type UseBlockerOptions, route, routeState, useBlocker, useIsPending, useLocation, useNavigate, useRouteData, useRouteParams, useRouteState, useSearchParams };
|
|
306
|
+
export { type ActionArgs, type ExtractRouteData, type ExtractRouteId, type ExtractRouteParams, type ExtractRouteState, type FallbackMode, type LoaderArgs, type Location, type MatchedRoute, type NavigateOptions, type OnNavigateCallback, type OnNavigateInfo, type OpaqueRouteDefinition, Outlet, type PathParams, type RouteComponentProps, type RouteComponentPropsOf, type RouteComponentPropsWithData, type RouteDefinition, Router, type RouterProps, type SSRConfig, type TypefulOpaqueRouteDefinition, type UseBlockerOptions, route, routeState, useBlocker, useIsPending, useLocation, useNavigate, useRouteData, useRouteParams, useRouteState, useSearchParams };
|
|
276
307
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/Router.tsx","../src/Outlet.tsx","../src/hooks/useNavigate.ts","../src/hooks/useLocation.ts","../src/hooks/useSearchParams.ts","../src/hooks/useBlocker.ts","../src/hooks/useRouteParams.ts","../src/hooks/useRouteState.ts","../src/hooks/useRouteData.ts","../src/hooks/useIsPending.ts"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/Router.tsx","../src/Outlet.tsx","../src/hooks/useNavigate.ts","../src/hooks/useLocation.ts","../src/hooks/useSearchParams.ts","../src/hooks/useBlocker.ts","../src/hooks/useRouteParams.ts","../src/hooks/useRouteState.ts","../src/hooks/useRouteData.ts","../src/hooks/useIsPending.ts"],"mappings":";;;;cAGM,6BAAA;;AAFoE;;;;;;;KAgB9D,uBAAA;EAAA,CACT,6BAAA,UAyB0B;EAvB3B,IAAA,WA4Be;EA1Bf,QAAA,GAAW,uBAAA;EAgCc;;;;;;EAzBzB,KAAA;EAPA;;;;;EAaA,eAAA,YAM2B;EAA3B,MAAA,IAAU,IAAA,EAAM,UAAA,CAAW,MAAA,+BAE3B;EAAA,MAAA,IAAU,IAAA,EAAM,UAAA,CAAW,MAAA,wCAAA;EAE3B,SAAA,GACI,aAAA;IACE,IAAA;IACA,MAAA,GAAS,MAAA;IACT,KAAA;IACA,QAAA,IACE,KAAA,cAAmB,IAAA,2BAChB,OAAA;IACL,YAAA,IAAgB,KAAA,cAAmB,IAAA;IACnC,UAAA,SAAmB,OAAA;IACnB,cAAA;IACA,IAAA;EAAA,KAEF,SAAA;AAAA;;;AAmBN;KAAY,YAAA;sCAEV,KAAA,EAAO,uBAAA,EAAP;EAEA,MAAA,EAAQ,MAAA,kBAAR;EAEA,QAAA;AAAA;;;;KAcU,cAAA;EAMQ,4DAJlB,OAAA,WAAkB,YAAA,WAUR;EARV,YAAA;EAEA,QAAA,EAAU,QAAA;AAAA;;;;KAMA,eAAA;EAYA,uDAVV,OAAA;EAEA,KAAA,YASA;EAPA,IAAA;AAAA;;;AAmBF;KAbY,QAAA;EACV,QAAA;EACA,MAAA;EACA,IAAA;AAAA;;;;;AAqBF;;;KAXY,kBAAA,IACV,KAAA,EAAO,aAAA,EACP,IAAA,EAAM,cAAA;;;;;ACvGR;;KDgHY,YAAA;;;;AAhJ8D;;KCgC9D,SAAA;ED9BkC;;AAc9C;;;;ECuBE,IAAA;EDC2B;;;;;;;;;;;ECW3B,UAAA;AAAA;AAAA,KAGU,WAAA;EACV,MAAA,EAAQ,eAAA;ED3BR;;;;;;;ECmCA,UAAA,GAAa,kBAAA;EDrBc;;;;;;EC4B3B,QAAA,GAAW,YAAA;EDtBL;;;;;;;;;;;;;;;AA4BR;;;;;ECeE,GAAA,GAAM,SAAA;AAAA;AAAA,iBAYQ,MAAA,CAAA;EACd,MAAA,EAAQ,WAAA;EACR,UAAA;EACA,QAAA;EACA;AAAA,GACC,WAAA,GAAc,SAAA;;;;;;AD5GyD;iBEM1D,MAAA,CAAA,GAAU,SAAA;;;;;;iBCAV,WAAA,CAAA,IAAgB,EAAA,UAAY,OAAA,GAAU,eAAA;;;;;;iBCAtC,WAAA,CAAA,GAAe,QAAA;;;KCJ1B,eAAA,IACH,MAAA,EACI,eAAA,GACA,MAAA,qBACE,IAAA,EAAM,eAAA,KAAoB,eAAA,GAAkB,MAAA;;;;iBAMpC,eAAA,CAAA,IAAoB,eAAA,EAAiB,eAAA;;;KCVzC,iBAAA;;;;ANF8D;EMOxE,WAAA;AAAA;;;ANSF;;;;;;;;;;;;;;;;;;;;;;;;;iBMqBgB,UAAA,CAAW,OAAA,EAAS,iBAAA;;;;;;ANrCsC;;;;;AAgB1E;;;;;;;;;;;;iBOSgB,cAAA,WACJ,4BAAA,SAER,MAAA,oCAAA,CAIF,KAAA,EAAO,CAAA,GAAI,kBAAA,CAAmB,CAAA;;;;;;APhC0C;;;;;AAgB1E;;;;;;;;;;;;;iBQUgB,aAAA,WACJ,4BAAA,SAER,MAAA,oCAAA,CAIF,KAAA,EAAO,CAAA,GAAI,iBAAA,CAAkB,CAAA;;;;;;ARjC2C;;;;;AAgB1E;;;;;;;;;;;;;;;;iBSagB,YAAA,WACJ,4BAAA,SAER,MAAA,oCAAA,CAIF,KAAA,EAAO,CAAA,GAAI,gBAAA,CAAiB,CAAA;;;;;;iBC/Bd,YAAA,CAAA"}
|