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 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).
@@ -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 };
@@ -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
+ }