Skip to content

Instantly share code, notes, and snippets.

@n1xx1
Created December 23, 2021 09:22
Show Gist options
  • Save n1xx1/461e10743796e48b805575d20265fee3 to your computer and use it in GitHub Desktop.
Save n1xx1/461e10743796e48b805575d20265fee3 to your computer and use it in GitHub Desktop.
Remix With Helmet and Chakra-UI
import { ChakraProvider } from "@chakra-ui/react";
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
hydrate(
<ChakraProvider>
<RemixBrowser />
</ChakraProvider>,
document.getElementById("wrapper")
);
import { ChakraProvider } from "@chakra-ui/react";
import { renderToString } from "react-dom/server";
import { Helmet } from "react-helmet";
import type { EntryContext } from "remix";
import { RemixServer } from "remix";
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
const markup = renderToString(
<ChakraProvider>
<RemixServer context={remixContext} url={request.url} />
</ChakraProvider>
);
const helmet = Helmet.renderStatic();
responseHeaders.set("Content-Type", "text/html");
const output = `<!DOCTYPE html>
<html lang="en" ${helmet.htmlAttributes.toString()}>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
${helmet.title.toString()}
${helmet.meta.toString()}
${helmet.link.toString()}
${helmet.style.toString()}
${helmet.script.toString()}
</head>
<body ${helmet.bodyAttributes.toString()}>
<div id="wrapper">${markup}</div>
</body>
</html>`;
return new Response(output, {
status: responseStatusCode,
headers: responseHeaders,
});
}
import { Box, Heading } from "@chakra-ui/react";
import { ReactNode } from "react";
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useCatch,
} from "remix";
import { Layout } from "./components/layout";
import { SturdyHelmet } from "./components/sturdy-helmet";
interface DocumentProps {
children: ReactNode;
title?: string;
}
function Document({ children, title }: DocumentProps) {
return (
<>
<SturdyHelmet>
{title ? <title>{title}</title> : null}
<Meta />
<Links />
<ScrollRestoration />
<Scripts />
<LiveReload />
</SturdyHelmet>
{children}
</>
);
}
export default function App() {
return (
<Document>
<Layout>
<Outlet />
</Layout>
</Document>
);
}
export function ErrorBoundary({ error }: { error: Error }) {
return (
<Document title="Error!">
<Box>
<Heading as="h1">There was an error</Heading>
</Box>
</Document>
);
}
export function CatchBoundary() {
let caught = useCatch();
return (
<Document title={`${caught.status} ${caught.statusText}`}>
<Box>
<Heading as="h1">
{caught.status} {caught.statusText}
</Heading>
</Box>
</Document>
);
}
import {
Children,
cloneElement,
Fragment,
HTMLAttributes,
isValidElement,
ReactNode,
} from "react";
import { Helmet, HelmetProps } from "react-helmet";
// custom solution to support deep nesting in helmet components
export function SturdyHelmet({ children }: HelmetProps) {
return <Helmet children={explodeFragment(children, "")} />;
}
function isReactFragment(node: any) {
if (node.type) {
return node.type === Fragment;
}
return node === Fragment;
}
function explodeFragment(n: ReactNode, prefix: string): ReactNode[] {
if (isReactFragment(n)) {
return (
Children.toArray((n as any).props.children).flatMap((c, i) =>
explodeFragment(c, `${prefix}-${i}`)
) ?? []
);
}
if (isValidElement<HTMLAttributes<HTMLElement>>(n)) {
// Helmet doesn't support dangerouslySetInnerHTML, but setting the children works fine.
if (n.props.dangerouslySetInnerHTML) {
return [
cloneElement(
n,
{ key: prefix },
n.props.dangerouslySetInnerHTML.__html
),
];
}
return [cloneElement(n, { key: prefix })];
}
return [];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment