Skip to content

Commit 2787855

Browse files
fix(react-router): (v7) fix static prerender non-ascii problem (#12161)
Co-authored-by: Jacob Ebey <[email protected]>
1 parent 756b43d commit 2787855

File tree

5 files changed

+100
-2
lines changed

5 files changed

+100
-2
lines changed

.changeset/shiny-cameras-try.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"integration": patch
3+
"@react-router/dev": patch
4+
"react-router": patch
5+
---
6+
7+
fix(react-router): (v7) fix static prerender of non-ascii characters

contributors.yml

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@
228228
- rubeonline
229229
- ryanflorence
230230
- ryanhiebert
231+
- saengmotmi
231232
- sanketshah19
232233
- saul-atomrigs
233234
- sbolel

integration/vite-prerender-test.ts

+87
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,93 @@ test.describe("Prerendering", () => {
560560
);
561561
});
562562

563+
test("Handles UTF-8 characters in prerendered and non-prerendered routes", async ({
564+
page,
565+
}) => {
566+
fixture = await createFixture({
567+
prerender: false,
568+
files: {
569+
...files,
570+
"vite.config.ts": js`
571+
import { defineConfig } from "vite";
572+
import { reactRouter } from "@react-router/dev/vite";
573+
574+
export default defineConfig({
575+
build: { manifest: true },
576+
plugins: [
577+
reactRouter({
578+
prerender: ["/", "/utf8-prerendered"],
579+
})
580+
],
581+
});
582+
`,
583+
"app/routes/utf8-prerendered.tsx": js`
584+
import { useLoaderData } from 'react-router';
585+
export function loader({ request }) {
586+
return {
587+
prerendered: request.headers.has('X-React-Router-Prerender') ? 'yes' : 'no',
588+
data: "한글 데이터 - UTF-8 문자",
589+
};
590+
}
591+
592+
export default function Comp() {
593+
let data = useLoaderData();
594+
return (
595+
<>
596+
<h1 data-title>UTF-8 Prerendered</h1>
597+
<p data-prerendered>{data.prerendered}</p>
598+
<p data-content>{data.data}</p>
599+
</>
600+
);
601+
}
602+
`,
603+
"app/routes/utf8-not-prerendered.tsx": js`
604+
import { useLoaderData } from 'react-router';
605+
export function loader({ request }) {
606+
return {
607+
prerendered: request.headers.has('X-React-Router-Prerender') ? 'yes' : 'no',
608+
data: "非プリレンダリングデータ - UTF-8文字",
609+
};
610+
}
611+
612+
export default function Comp() {
613+
let data = useLoaderData();
614+
return (
615+
<>
616+
<h1 data-title>UTF-8 Not Prerendered</h1>
617+
<p data-prerendered>{data.prerendered}</p>
618+
<p data-content>{data.data}</p>
619+
</>
620+
);
621+
}
622+
`,
623+
},
624+
});
625+
appFixture = await createAppFixture(fixture);
626+
627+
let app = new PlaywrightFixture(appFixture, page);
628+
629+
// Test prerendered route with UTF-8 characters
630+
await app.goto("/utf8-prerendered");
631+
await page.waitForSelector("[data-mounted]");
632+
expect(await app.getHtml("[data-title]")).toContain("UTF-8 Prerendered");
633+
expect(await app.getHtml("[data-prerendered]")).toContain("yes");
634+
expect(await app.getHtml("[data-content]")).toContain(
635+
"한글 데이터 - UTF-8 문자"
636+
);
637+
638+
// Test non-prerendered route with UTF-8 characters
639+
await app.goto("/utf8-not-prerendered");
640+
await page.waitForSelector("[data-mounted]");
641+
expect(await app.getHtml("[data-title]")).toContain(
642+
"UTF-8 Not Prerendered"
643+
);
644+
expect(await app.getHtml("[data-prerendered]")).toContain("no");
645+
expect(await app.getHtml("[data-content]")).toContain(
646+
"非プリレンダリングデータ - UTF-8文字"
647+
);
648+
});
649+
563650
test("Renders down to the proper HydrateFallback", async ({ page }) => {
564651
fixture = await createFixture({
565652
prerender: true,

packages/react-router-dev/vite/plugin.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1888,7 +1888,7 @@ async function handlePrerender(
18881888
reactRouterConfig,
18891889
viteConfig,
18901890
data
1891-
? { headers: { ...headers, "X-React-Router-Prerender-Data": data } }
1891+
? { headers: { ...headers, "X-React-Router-Prerender-Data": encodeURI(data) } }
18921892
: { headers }
18931893
);
18941894
}

packages/react-router/lib/server-runtime/routes.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,12 @@ export function createStaticHandlerDataRoutes(
7979
// If we're prerendering, use the data passed in from prerendering
8080
// the .data route so we dom't call loaders twice
8181
if (args.request.headers.has("X-React-Router-Prerender-Data")) {
82-
let encoded = args.request.headers.get(
82+
const preRenderedData = args.request.headers.get(
8383
"X-React-Router-Prerender-Data"
8484
);
85+
let encoded = preRenderedData
86+
? decodeURI(preRenderedData)
87+
: preRenderedData;
8588
invariant(encoded, "Missing prerendered data for route");
8689
let uint8array = new TextEncoder().encode(encoded);
8790
let stream = new ReadableStream({

0 commit comments

Comments
 (0)