-
A proxy server is injecting HTML to my app based on placeholder comments, like a CMS, so hydrating the entire document won't work for me because it deletes that content. (Unfortunately the CMS doesn't have an API that I could use.) What could work best for my case is for Remix to manage only a part of the document, while the rest remains static. Basically the <!-- app/template.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- app-head -->
</head>
<body>
<!-- CMS_FEATURE header --><!-- CMS_FEATURE_END header -->
<div id="root"><!-- app-body --></div>
<!-- CMS_FEATURE footer --><!-- CMS_FEATURE_END footer -->
</body>
</html> However, as you might have guessed, my challenge is the // app/root.tsx
import type { MetaFunction, LinksFunction } from '@remix-run/node';
import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from '@remix-run/react';
import tailwindUrl from './tailwind.css?url';
export const meta: MetaFunction = () => [
{ title: 'My Partial Site' },
];
export const links: LinksFunction = () => [
{ rel: 'stylesheet', href: tailwindUrl },
];
export function Layout({ children }: { children: React.ReactNode }) {
return (
<>
{/* these two need to be placed in <head> */}
<Meta />
<Links />
{/* the rest of this is fine as-is */}
{children}
<ScrollRestoration />
<Scripts />
</>
);
}
export default function App() {
return <Outlet />;
} I'm also totally inexperienced with streaming, so this is as far as I got in my // app/entry.server.tsx
import { PassThrough } from 'node:stream';
import fs from 'node:fs';
import type { EntryContext } from '@remix-run/node';
import { createReadableStreamFromReadable } from '@remix-run/node';
import { RemixServer } from '@remix-run/react';
import { renderToPipeableStream } from 'react-dom/server';
const ABORT_DELAY = 5_000;
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
) {
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<RemixServer
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
{
onShellReady() {
shellRendered = true;
const templateHtml = fs.readFileSync('./app/template.html');
const [templateHtmlStart, templateHtmlEnd] =
templateHtml.split('<!-- app-body -->');
const body = new PassThrough();
const stream = createReadableStreamFromReadable(body);
responseHeaders.set('Content-Type', 'text/html');
resolve(
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
}),
);
body.write(templateHtmlStart);
pipe(body);
body.write(templateHtmlEnd);
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
responseStatusCode = 500;
if (shellRendered) {
console.error(error);
}
},
},
);
setTimeout(abort, ABORT_DELAY);
});
} Streaming in the body of the app works this way, not sure if I did it correctly or not, but it does. Now, is it possible to somehow render |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Maybe you'll find remix-island's code helpful. https://github.com/Xiphe/remix-island |
Beta Was this translation helpful? Give feedback.
Maybe you'll find remix-island's code helpful. https://github.com/Xiphe/remix-island