vike-lite-react 1.0.0
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 +148 -0
- package/dist/index.d.mts +20 -0
- package/dist/index.mjs +40 -0
- package/dist/vite.d.mts +17 -0
- package/dist/vite.mjs +24 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# vike-lite-react
|
|
2
|
+
|
|
3
|
+
The official React integration for `vike-lite`. It provides seamless Server-Side Rendering (SSR), Static Site Generation (SSG), and client hydration out of the box, with a focus on minimalism and performance.
|
|
4
|
+
|
|
5
|
+
### ⚙️ Install
|
|
6
|
+
You need to install both `vike-lite-react` and the official Vite plugin for React (`@vitejs/plugin-react`).
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
# npm
|
|
10
|
+
npm install -D vike-lite-react @vitejs/plugin-react
|
|
11
|
+
npm install react react-dom
|
|
12
|
+
|
|
13
|
+
# pnpm
|
|
14
|
+
pnpm add -D vike-lite-react @vitejs/plugin-react
|
|
15
|
+
pnpm add react react-dom
|
|
16
|
+
|
|
17
|
+
# yarn
|
|
18
|
+
yarn add -D vike-lite-react @vitejs/plugin-react
|
|
19
|
+
yarn add react react-dom
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### ⚙️ Vite Plugin
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// vite.config.ts
|
|
26
|
+
import type { UserConfig } from 'vite'
|
|
27
|
+
import vikeLite from 'vike-lite/vite'
|
|
28
|
+
import vikeLiteReact from 'vike-lite-react/vite'
|
|
29
|
+
|
|
30
|
+
export default {
|
|
31
|
+
plugins: [
|
|
32
|
+
vikeLite(),
|
|
33
|
+
vikeLiteReact({
|
|
34
|
+
// Default is `true` that enables React Hydration
|
|
35
|
+
// Set to `false` for Client Takeover (SPA mode)
|
|
36
|
+
hydration: true,
|
|
37
|
+
// Advanced: pass options directly to the underlying @vitejs/plugin-react
|
|
38
|
+
react: {
|
|
39
|
+
babel: {
|
|
40
|
+
plugins: [
|
|
41
|
+
// e.g. add custom babel plugins
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
]
|
|
47
|
+
} satisfies UserConfig
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
| Option | Type | Default | Description
|
|
51
|
+
| - | - | - | -
|
|
52
|
+
| `hydration` | `boolean` | `true` | When `true`, the server renders the page to HTML and the client hydrates it (`hydrateRoot`). When `false`, the client discards the server-rendered HTML on load and mounts a fresh tree (`createRoot`) — useful for highly interactive pages where paying the hydration-mismatch tax isn't worth it.
|
|
53
|
+
| `react` | `Options` (from `@vitejs/plugin-react`) | `{}` | Passed through to the underlying `@vitejs/plugin-react` instance. Use this for `jsxImportSource` (e.g. Emotion), custom Babel plugins, or `jsxRuntime: 'classic'`.
|
|
54
|
+
|
|
55
|
+
### 🪝 Hooks
|
|
56
|
+
|
|
57
|
+
#### `useData`
|
|
58
|
+
Access the data fetched by your `+data` functions directly inside your React components.
|
|
59
|
+
```tsx
|
|
60
|
+
// /pages/+Page.tsx
|
|
61
|
+
import { useData } from 'vike-lite-react'
|
|
62
|
+
|
|
63
|
+
type MyData = {
|
|
64
|
+
title: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default function Page() {
|
|
68
|
+
const [data, setData] = useData<MyData>()
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div>
|
|
72
|
+
<h1>{data.title}</h1>
|
|
73
|
+
<button onClick={() => setData(prev => ({ ...prev, title: 'Updated Title!' }))}>
|
|
74
|
+
Update Data
|
|
75
|
+
</button>
|
|
76
|
+
</div>
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> 💡 **Note:** Like `vike-lite-solid`, the `useData` hook in `vike-lite-react` returns a tuple `[data, setData]`, letting you mutate the route data locally without needing an extra state manager.
|
|
82
|
+
|
|
83
|
+
#### `usePageContext`
|
|
84
|
+
Access the current page context, including URL parameters, original pathname, and route information.
|
|
85
|
+
```tsx
|
|
86
|
+
// /pages/+Page.tsx
|
|
87
|
+
import { usePageContext } from 'vike-lite-react'
|
|
88
|
+
|
|
89
|
+
export function Page() {
|
|
90
|
+
const pageContext = usePageContext()
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div>
|
|
94
|
+
<p>Current Path: <strong>{pageContext.urlPathname}</strong></p>
|
|
95
|
+
</div>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### `useHydrated`
|
|
101
|
+
Detect whether the application has successfully hydrated on the client. Essential for wrapping client-only libraries (like chart tools or window-dependent logic) to avoid SSR hydration mismatches.
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
// /pages/+Page.tsx
|
|
105
|
+
import { useHydrated } from 'vike-lite-react'
|
|
106
|
+
import ClientOnlyChart from './Chart'
|
|
107
|
+
|
|
108
|
+
export function Page() {
|
|
109
|
+
const hydrated = useHydrated()
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div>
|
|
113
|
+
<h1>Statistics</h1>
|
|
114
|
+
{hydrated ? <ClientOnlyChart /> : <p>Loading chart…</p>}
|
|
115
|
+
</div>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### `useUrl`
|
|
121
|
+
```tsx
|
|
122
|
+
// /pages/+Page.tsx
|
|
123
|
+
import { useUrl } from 'vike-lite-react'
|
|
124
|
+
|
|
125
|
+
export function Page() {
|
|
126
|
+
const url = useUrl()
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div>
|
|
130
|
+
<p>Current Query Parameter "myQueryParam": <strong>{url.searchParams.get('myQueryParam')}</strong></p>
|
|
131
|
+
</div>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Differences: `vike-react` vs `vike-lite-react`
|
|
137
|
+
Why choose `vike-lite`? It's built to be as minimal and fast as possible. Here are the main architectural differences regarding the React integration:
|
|
138
|
+
|
|
139
|
+
| **Feature** | `vike-react` | `vike-lite-react` | **Why it matters**
|
|
140
|
+
| - | - | - | -
|
|
141
|
+
| **Reactivity Architecture** | _Single Source of Truth_ | _Separation of Concerns_ | `vike-lite-react` keeps page data (`pageContext`) and the active UI (`view`: Page/Layout/Head) as two separate `useState` atoms, so a data update doesn't force React to re-resolve which components are mounted, and vice versa.
|
|
142
|
+
| **Accessibility (A11y)** | _Not_ handled by default | _Automatic_ handled | After a client-side navigation, `vike-lite-react` moves the focus to `#root`. This significantly improves UX for keyboard navigation and screen readers.
|
|
143
|
+
| `useData()` **Hook** | `getter` only | `[getter, setter]` | `vike-lite-react` allows you to mutate the route data locally without needing other state managers.
|
|
144
|
+
| URL parsed | manual `new URL(pageContext.urlOriginal)` | [useUrl()](#useurl) | A dedicated hook, consistent with `vike-lite-solid`.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
This project is licensed under the [MIT License](../../LICENSE).
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PageContext, PageContextClient } from "vike-lite";
|
|
2
|
+
//#region src/hooks/useData.d.ts
|
|
3
|
+
declare function useData<Data = unknown>(): [Data, (updater: Data | ((prev: Data) => Data)) => void];
|
|
4
|
+
//#endregion
|
|
5
|
+
//#region src/hooks/usePageContext.d.ts
|
|
6
|
+
declare function usePageContext(): PageContext;
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/hooks/useHydrated.d.ts
|
|
9
|
+
declare function useHydrated(): boolean;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/hooks/useUrl.d.ts
|
|
12
|
+
declare function useUrl(): URL;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/hooks/globalContext.d.ts
|
|
15
|
+
interface PageContextValue {
|
|
16
|
+
pageContext: PageContextClient;
|
|
17
|
+
setPageContext: (updater: (prev: PageContextClient) => PageContextClient) => void;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { type PageContextValue, useData, useHydrated, usePageContext, useUrl };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
2
|
+
//#region src/hooks/globalContext.tsx
|
|
3
|
+
const KEY = "__vike_lite_react_context__";
|
|
4
|
+
const g = globalThis;
|
|
5
|
+
if (!Object.hasOwn(g, KEY)) g[KEY] = createContext(null);
|
|
6
|
+
const PageContextReactContext = g[KEY];
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/hooks/useData.tsx
|
|
9
|
+
function useData() {
|
|
10
|
+
const ctx = useContext(PageContextReactContext);
|
|
11
|
+
if (!ctx) throw new Error("useData() must be called inside a page rendered by vike-lite-react");
|
|
12
|
+
return [ctx.pageContext.data, useCallback((updater) => {
|
|
13
|
+
ctx.setPageContext((prev) => ({
|
|
14
|
+
...prev,
|
|
15
|
+
data: typeof updater === "function" ? updater(prev.data) : updater
|
|
16
|
+
}));
|
|
17
|
+
}, [ctx])];
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/hooks/usePageContext.tsx
|
|
21
|
+
function usePageContext() {
|
|
22
|
+
const ctx = useContext(PageContextReactContext);
|
|
23
|
+
if (!ctx) throw new Error("usePageContext() must be called inside a page rendered by vike-lite-react");
|
|
24
|
+
return ctx.pageContext;
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/hooks/useHydrated.tsx
|
|
28
|
+
function useHydrated() {
|
|
29
|
+
const [hydrated, setHydrated] = useState(false);
|
|
30
|
+
useEffect(() => setHydrated(true), []);
|
|
31
|
+
return hydrated;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/hooks/useUrl.tsx
|
|
35
|
+
function useUrl() {
|
|
36
|
+
const pageContext = usePageContext();
|
|
37
|
+
return useMemo(() => new URL(pageContext.urlOriginal), [pageContext.urlOriginal]);
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { useData, useHydrated, usePageContext, useUrl };
|
package/dist/vite.d.mts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Options } from "@vitejs/plugin-react";
|
|
2
|
+
import { Plugin } from "vite";
|
|
3
|
+
//#region src/vite-plugin.d.ts
|
|
4
|
+
interface VikeLiteReactOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Enable client hydration (SSR + hydrate) or full client takeover (SPA mode).
|
|
7
|
+
* @default true
|
|
8
|
+
*/
|
|
9
|
+
hydration?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Advanced: pass options directly to the underlying @vitejs/plugin-react.
|
|
12
|
+
*/
|
|
13
|
+
react?: Options;
|
|
14
|
+
}
|
|
15
|
+
declare function vikeLiteReact(options?: VikeLiteReactOptions): Plugin[];
|
|
16
|
+
//#endregion
|
|
17
|
+
export { VikeLiteReactOptions, vikeLiteReact as default };
|
package/dist/vite.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import react from "@vitejs/plugin-react";
|
|
2
|
+
//#region src/vite-plugin.ts
|
|
3
|
+
function vikeLiteReact(options = {}) {
|
|
4
|
+
const { hydration = true, react: reactOptions } = options;
|
|
5
|
+
const virtualConfigId = "virtual:vike-lite/config";
|
|
6
|
+
const virtualRendererId = "virtual:vike-lite/renderer";
|
|
7
|
+
const resolvedVirtualConfigId = "\0virtual:vike-lite/config";
|
|
8
|
+
const resolvedVirtualRendererId = "\0virtual:vike-lite/renderer";
|
|
9
|
+
const adapter = {
|
|
10
|
+
name: "vike-lite-react",
|
|
11
|
+
enforce: "pre",
|
|
12
|
+
resolveId(id) {
|
|
13
|
+
if (id === virtualConfigId) return resolvedVirtualConfigId;
|
|
14
|
+
if (id === virtualRendererId) return resolvedVirtualRendererId;
|
|
15
|
+
},
|
|
16
|
+
load(id) {
|
|
17
|
+
if (id === resolvedVirtualConfigId) return `export const hydration = ${JSON.stringify(hydration)};`;
|
|
18
|
+
if (id === resolvedVirtualRendererId) return "export const onRenderHtml = () => import('vike-lite-react/__internal/server/onRenderHtml');export const onRenderClient = () => import('vike-lite-react/__internal/client/onRenderClient');";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
return [...react(reactOptions), adapter];
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { vikeLiteReact as default };
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vike-lite-react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.mts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.mts",
|
|
11
|
+
"import": "./dist/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"./vite": {
|
|
14
|
+
"types": "./dist/vite.d.mts",
|
|
15
|
+
"import": "./dist/vite.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./__internal/client/onRenderClient": {
|
|
18
|
+
"types": "./src/__internal/client/onRenderClient.tsx",
|
|
19
|
+
"import": "./src/__internal/client/onRenderClient.tsx"
|
|
20
|
+
},
|
|
21
|
+
"./__internal/server/onRenderHtml": {
|
|
22
|
+
"types": "./src/__internal/server/onRenderHtml.tsx",
|
|
23
|
+
"import": "./src/__internal/server/onRenderHtml.tsx"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/node-ecosystem/vike-lite-react.git"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/node-ecosystem/vike-lite-react#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/node-ecosystem/vike-lite-react/issues"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist"
|
|
36
|
+
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"tsc": "tsc --noEmit",
|
|
39
|
+
"build": "tsdown"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/node": "^26.1.1",
|
|
43
|
+
"@types/react": "^19.2.17",
|
|
44
|
+
"@types/react-dom": "^19.2.3",
|
|
45
|
+
"@vitejs/plugin-react": "^6.0.3",
|
|
46
|
+
"react": "^19.2.7",
|
|
47
|
+
"react-dom": "^19.2.7",
|
|
48
|
+
"tsdown": "^0.22.5",
|
|
49
|
+
"vike-lite": "1.13.0",
|
|
50
|
+
"vite": "^8.1.4"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@vitejs/plugin-react": ">=6",
|
|
54
|
+
"react": "*",
|
|
55
|
+
"react-dom": "*",
|
|
56
|
+
"vike-lite": "*",
|
|
57
|
+
"vite": ">=8"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
61
|
+
}
|
|
62
|
+
}
|