diff --git a/package.json b/package.json index f1e9965..9d42a7d 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "remix-hono": "^0.0.16", + "remix-island": "^0.2.0", "zod": "^3.23.8" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index faf196f..063090d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ dependencies: remix-hono: specifier: ^0.0.16 version: 0.0.16(typescript@5.4.5)(zod@3.23.8) + remix-island: + specifier: ^0.2.0 + version: 0.2.0(@remix-run/react@2.13.1)(@remix-run/server-runtime@2.13.1)(react-dom@18.3.1)(react@18.3.1) zod: specifier: ^3.23.8 version: 3.23.8 @@ -6745,6 +6748,20 @@ packages: - typescript dev: false + /remix-island@0.2.0(@remix-run/react@2.13.1)(@remix-run/server-runtime@2.13.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-NujWtmulgupxNOMiWKAj8lg56eYsy09aV/2pML8rov8N8LmY1UnSml4XYad+KHLy/pgZ1D9UxAmjI6GBJydTUg==} + peerDependencies: + '@remix-run/react': '>= 1' + '@remix-run/server-runtime': '>= 1' + react: '>= 16.8' + react-dom: '>= 16.8' + dependencies: + '@remix-run/react': 2.13.1(react-dom@18.3.1)(react@18.3.1)(typescript@5.4.5) + '@remix-run/server-runtime': 2.13.1(typescript@5.4.5) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + dev: false + /require-like@0.1.2: resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} dev: true diff --git a/src/remix-app/Document.tsx b/src/remix-app/Document.tsx new file mode 100644 index 0000000..f9e668e --- /dev/null +++ b/src/remix-app/Document.tsx @@ -0,0 +1,30 @@ +import { Links, Meta, Scripts, ScrollRestoration } from '@remix-run/react'; +import { createHead } from 'remix-island'; + +import { useNonce } from './hooks/nonce'; + +import type { PropsWithChildren } from 'react'; + +const Head = createHead(() => ( + <> + + + + +)); + +const Document = ({ children }: PropsWithChildren) => { + const nonce = useNonce(); + + return ( + <> + + {children} + + + + + ); +}; + +export { Document, Head }; diff --git a/src/remix-app/entry.client.tsx b/src/remix-app/entry.client.tsx index 871cad8..141bf96 100644 --- a/src/remix-app/entry.client.tsx +++ b/src/remix-app/entry.client.tsx @@ -4,7 +4,7 @@ import { hydrateRoot } from 'react-dom/client'; startTransition(() => { hydrateRoot( - document, + document.getElementById('root')!, diff --git a/src/remix-app/entry.server.tsx b/src/remix-app/entry.server.tsx index 4742a02..43aca37 100644 --- a/src/remix-app/entry.server.tsx +++ b/src/remix-app/entry.server.tsx @@ -4,11 +4,18 @@ import { createReadableStreamFromReadable } from '@remix-run/node'; import { RemixServer } from '@remix-run/react'; import { isbot } from 'isbot'; import { renderToPipeableStream } from 'react-dom/server'; +import { renderHeadToString } from 'remix-island'; +import { Head } from './Document'; import NonceProvider from './hooks/nonce'; import type { AppLoadContext, EntryContext } from '@remix-run/node'; +const CLOSING_HTML = '' as const; + +const generateOpeningHTML = (nonce: string, headStr: string) => + `${headStr}
`; + const handleRequest = ( request: Request, responseStatusCode: number, @@ -36,6 +43,7 @@ const handleBotRequest = ( { onAllReady() { shellRendered = true; + const headStr = renderHeadToString({ request, remixContext, Head }); const body = new PassThrough(); const stream = createReadableStreamFromReadable(body); @@ -48,7 +56,9 @@ const handleBotRequest = ( }) ); + body.write(generateOpeningHTML(loadContext.nonce, headStr)); pipe(body); + body.write(CLOSING_HTML); }, onShellError(error: unknown) { reject(error); @@ -84,6 +94,7 @@ const handleBrowserRequest = ( { onShellReady() { shellRendered = true; + const headStr = renderHeadToString({ request, remixContext, Head }); const body = new PassThrough(); const stream = createReadableStreamFromReadable(body); @@ -96,7 +107,9 @@ const handleBrowserRequest = ( }) ); + body.write(generateOpeningHTML(loadContext.nonce, headStr)); pipe(body); + body.write(CLOSING_HTML); }, onShellError(error: unknown) { reject(error); diff --git a/src/remix-app/root.tsx b/src/remix-app/root.tsx index 822aba2..4b2158e 100644 --- a/src/remix-app/root.tsx +++ b/src/remix-app/root.tsx @@ -1,6 +1,6 @@ -import { Links, Meta, Outlet, Scripts, ScrollRestoration } from '@remix-run/react'; +import { Outlet } from '@remix-run/react'; -import { useNonce } from './hooks/nonce'; +import { Document } from './Document'; import stylexStylesheet from './main.css?url'; import type { LinksFunction } from '@remix-run/node'; @@ -12,27 +12,11 @@ const links: LinksFunction = () => [ }, ]; -const App = () => { - const nonce = useNonce(); - - return ( - - - - - - - - - - - - - - - - ); -}; +const App = () => ( + + + +); export default App; export { links };