@alxia/react-router 0.12.0 → 0.12.1
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 +13 -6
- package/docs/guide/csp-nonce.md +7 -5
- package/docs/guide/document.md +16 -5
- package/docs/troubleshooting.md +35 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -300,12 +300,17 @@ paint:
|
|
|
300
300
|
```tsx
|
|
301
301
|
// app/root.tsx
|
|
302
302
|
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from 'react-router';
|
|
303
|
-
import {
|
|
303
|
+
import {
|
|
304
|
+
CspNonceMeta,
|
|
305
|
+
InlineScript,
|
|
306
|
+
useNonce,
|
|
307
|
+
} from '@alxia/react-router/document';
|
|
304
308
|
import { themeBootScript } from '<your theme library>';
|
|
305
309
|
|
|
306
310
|
const THEME_BOOT = themeBootScript();
|
|
307
311
|
|
|
308
312
|
export function Layout({ children }: { children: React.ReactNode }) {
|
|
313
|
+
const nonce = useNonce();
|
|
309
314
|
return (
|
|
310
315
|
<html lang="en" suppressHydrationWarning>
|
|
311
316
|
<head>
|
|
@@ -313,7 +318,7 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
313
318
|
<InlineScript code={THEME_BOOT} />
|
|
314
319
|
<CspNonceMeta />
|
|
315
320
|
<Meta />
|
|
316
|
-
<Links />
|
|
321
|
+
<Links nonce={nonce} />
|
|
317
322
|
</head>
|
|
318
323
|
<body>
|
|
319
324
|
{children}
|
|
@@ -329,10 +334,12 @@ export default function App() {
|
|
|
329
334
|
}
|
|
330
335
|
```
|
|
331
336
|
|
|
332
|
-
`THEME_BOOT` is your theme library's own boot script. With `/entry`,
|
|
333
|
-
`<Scripts>` and `<ScrollRestoration>` take the nonce from `<ServerRouter nonce
|
|
334
|
-
|
|
335
|
-
|
|
337
|
+
`THEME_BOOT` is your theme library's own boot script. With `/entry`,
|
|
338
|
+
`<Scripts>` and `<ScrollRestoration>` take the nonce from `<ServerRouter nonce>`
|
|
339
|
+
and need no prop. `<Links>` does not suppress the hydration warning for its
|
|
340
|
+
critical-CSS `<style nonce>` in development, so pass it `useNonce()`, as above;
|
|
341
|
+
`useNonce()` also serves `InlineScript`, `CspNonceMeta` and any script or style
|
|
342
|
+
the app inserts. An entry of your own
|
|
336
343
|
wraps the router:
|
|
337
344
|
|
|
338
345
|
```tsx
|
package/docs/guide/csp-nonce.md
CHANGED
|
@@ -64,11 +64,13 @@ export {
|
|
|
64
64
|
It reads `nonceOf(loadContext)` by default; `createEntry({ nonce })`
|
|
65
65
|
takes another source ([the entry](document.md#the-entry)).
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`
|
|
67
|
+
`<Scripts>` and `<ScrollRestoration>` fall back to the nonce `<ServerRouter
|
|
68
|
+
nonce>` put in React Router's framework context, so they need no `nonce` prop
|
|
69
|
+
with this entry. `<Links>` needs `<Links nonce={useNonce()} />`: it does not
|
|
70
|
+
suppress the hydration warning for its development critical-CSS `<style>`
|
|
71
|
+
([why](document.md#what-react-router-renders)). `useNonce()` also serves what
|
|
72
|
+
the app inserts itself: an `InlineScript`, a `CspNonceMeta`, any other script
|
|
73
|
+
or style.
|
|
72
74
|
|
|
73
75
|
### An entry of your own
|
|
74
76
|
|
package/docs/guide/document.md
CHANGED
|
@@ -61,19 +61,23 @@ tests: reset modules between them, or render the server path with
|
|
|
61
61
|
|
|
62
62
|
## What React Router renders
|
|
63
63
|
|
|
64
|
-
With `@alxia/react-router/entry`,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
With `@alxia/react-router/entry`, `<Scripts>` and `<ScrollRestoration>` need
|
|
65
|
+
no `nonce`: they fall back to the nonce `<ServerRouter nonce>` put in React
|
|
66
|
+
Router's framework context, and they suppress the hydration warning for the
|
|
67
|
+
attribute the browser hides. `<Links>` falls back to it on the server too, but
|
|
68
|
+
does not suppress the warning, so pass it `useNonce()`:
|
|
68
69
|
|
|
69
70
|
```tsx
|
|
70
71
|
// app/root.tsx
|
|
72
|
+
import { useNonce } from '@alxia/react-router/document';
|
|
73
|
+
|
|
71
74
|
export function Layout({ children }: { children: React.ReactNode }) {
|
|
75
|
+
const nonce = useNonce();
|
|
72
76
|
return (
|
|
73
77
|
<html lang="en">
|
|
74
78
|
<head>
|
|
75
79
|
<meta charSet="utf-8" />
|
|
76
|
-
<Links />
|
|
80
|
+
<Links nonce={nonce} />
|
|
77
81
|
</head>
|
|
78
82
|
<body>
|
|
79
83
|
{children}
|
|
@@ -85,6 +89,13 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
85
89
|
}
|
|
86
90
|
```
|
|
87
91
|
|
|
92
|
+
Why `<Links>` needs it: in development, React Router renders the critical CSS
|
|
93
|
+
as a `<style nonce>`. The server writes the nonce on it, the browser hides the
|
|
94
|
+
attribute and React Router's client knows none, so without the prop the client
|
|
95
|
+
renders the `<style>` with no nonce and React reports a mismatch
|
|
96
|
+
([Troubleshooting](../troubleshooting.md#a-tree-hydrated-but-some-attributes-of-the-server-rendered-html-didnt-match-the-client-properties-on-a-style-with-a-nonce)).
|
|
97
|
+
`useNonce()` returns the same value on both sides.
|
|
98
|
+
|
|
88
99
|
Keep `useNonce()` for what React Router does not render: an `InlineScript`,
|
|
89
100
|
a `CspNonceMeta`, and any script or style the app inserts itself.
|
|
90
101
|
|
package/docs/troubleshooting.md
CHANGED
|
@@ -28,6 +28,7 @@ a loader, a message React Router or the browser prints, or an error from
|
|
|
28
28
|
- [`Refused to execute inline script because it violates the following Content Security Policy directive: "default-src 'none'"`](#refused-to-execute-inline-script-because-it-violates-the-following-content-security-policy-directive-default-src-none)
|
|
29
29
|
- [`Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'nonce-…'"`](#refused-to-execute-inline-script-because-it-violates-the-following-content-security-policy-directive-script-src-self-nonce-)
|
|
30
30
|
- [`A tree hydrated but some attributes of the server rendered HTML didn't match the client properties`](#a-tree-hydrated-but-some-attributes-of-the-server-rendered-html-didnt-match-the-client-properties)
|
|
31
|
+
- [`A tree hydrated but some attributes of the server rendered HTML didn't match the client properties` on a `<style>` with a `nonce`](#a-tree-hydrated-but-some-attributes-of-the-server-rendered-html-didnt-match-the-client-properties-on-a-style-with-a-nonce)
|
|
31
32
|
- [`useNonce()` is undefined on the server](#usenonce-is-undefined-on-the-server)
|
|
32
33
|
- [`TypeError: InlineScript(): the code contains "</script" or "<!--", which ends or confuses the script element; escape it, such as "<\/script" inside a string`](#typeerror-inlinescript-the-code-contains-script-or----which-ends-or-confuses-the-script-element-escape-it-such-as-script-inside-a-string)
|
|
33
34
|
- [`TypeError: createPageLanguage(): the fallback … is not one of the languages; got ….`](#typeerror-createpagelanguage-the-fallback--is-not-one-of-the-languages-got-)
|
|
@@ -603,6 +604,40 @@ A warning on another element is a real mismatch: a `nonce` the browser hid
|
|
|
603
604
|
is read by `useNonce()` from the document, so never pass it from loader
|
|
604
605
|
data ([guide](guide/document.md#the-theme-boot-script)).
|
|
605
606
|
|
|
607
|
+
### `A tree hydrated but some attributes of the server rendered HTML didn't match the client properties` on a `<style>` with a `nonce`
|
|
608
|
+
|
|
609
|
+
**When:** in development (`react-router dev`) with a CSP nonce, the console
|
|
610
|
+
warns that a tree hydrated but some attributes did not match, and the diff
|
|
611
|
+
shows `nonce="…"` on the `<style data-react-router-critical-css>` in `<head>`.
|
|
612
|
+
|
|
613
|
+
**Why:** React Router renders the critical CSS as a `<style nonce>`. The
|
|
614
|
+
server writes the nonce from `<ServerRouter nonce>`, the browser hides the
|
|
615
|
+
attribute, and `<Links />` has no nonce to render on the client. Unlike
|
|
616
|
+
`<Scripts>` and `<ScrollRestoration>`, `<Links>` does not suppress that
|
|
617
|
+
warning.
|
|
618
|
+
|
|
619
|
+
**Fix:** pass it the nonce `useNonce()` reads, on both sides:
|
|
620
|
+
|
|
621
|
+
```tsx
|
|
622
|
+
// app/root.tsx
|
|
623
|
+
import { useNonce } from '@alxia/react-router/document';
|
|
624
|
+
|
|
625
|
+
export function Layout({ children }: { children: React.ReactNode }) {
|
|
626
|
+
const nonce = useNonce();
|
|
627
|
+
return (
|
|
628
|
+
<html lang="en">
|
|
629
|
+
<head>
|
|
630
|
+
<Links nonce={nonce} />
|
|
631
|
+
</head>
|
|
632
|
+
<body>{children}</body>
|
|
633
|
+
</html>
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
`<Scripts>` and `<ScrollRestoration>` need nothing
|
|
639
|
+
([guide](guide/document.md#what-react-router-renders)).
|
|
640
|
+
|
|
606
641
|
### `useNonce()` is undefined on the server
|
|
607
642
|
|
|
608
643
|
**When:** the page's inline scripts or `<CspNonceMeta />` render with no
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alxia/react-router",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"description": "alxia as the server of a React Router framework app: server rendering behind the app's hooks, loaders reading its typed context, the client's assets served; the code with no UI an installable app shares (`/pwa`), its hooks (`/pwa/react`), `noCache` for the worker's script (`/pwa/server`), the service worker on Workbox (`/pwa/worker`) and its build (`/pwa/build`), an API's refusals worded for the page (`/errors`), the page's CSP nonce and inline boot scripts (`/document`) and a streaming `entry.server` (`/entry`)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|