S
SolidJSā€¢10mo ago
andrezero

SSR/SSG: How to share context between client and server

I am trying to implement a Design System of sorts using SolidJS (my prior experience is with React) and ran into this challenge:
How can we provide dynamic context that is both available when rendering document <html> as well as to components that need to consume it at both SSR and CSR time?
I was able to wrap the server side rendered document with a provider and everything works fine here šŸ„³ (which means that most of the features I am looking at will be straightforward to implement). But the context is not available anywhere inside the start-client tree. Is there a known strategy to achieve this? Thanks in advance šŸ™‡ā€ā™‚ļø
6 Replies
andrezero
andrezeroOPā€¢10mo ago
Easy to reproduce adding these 3 files to the Solid Start basic template.
ryansolid
ryansolidā€¢10mo ago
I see.. it's because the Document doesn't hydrate. To support CSR mode I needed to break it out. This complicates things a bit because your signal would never trigger update anyway even if we made it available higher up through middleware. So you want to put something on the body? The easiest way to do it would be server render it non dynamic and use an effect to write and update it in the browser.
andrezero
andrezeroOPā€¢10mo ago
Thanks for answering. I was suspecting/fearing this šŸ˜¬ The (technical) use case(s) are indeed to have control over <html/body class> and/or style and inject <style tags> and/or other assets into the document based on route/context. Two business cases for this, that I have encountered recently - multi-brand design systems - A/B testing design system iterations - "theme" variations based on feature flags In these scenarios, the SSR time rendering is a requirement to prevent "flashes of..." and to guarantee overall šŸŽ© tip top core metrics and user experience. Note: There are other business cases for having full dynamic control of the document. šŸ¤” This is typically achievable via the render content > render document pipeline, as in ReactHelmet ... <MetaProvider>...<Meta/>. ā“ Is there anything fundamental / architectural that I'm missing that prevents generalising this approach so that one could do something like
type DocumentProps = {
assets: JSX.Element;
children: JSX.Element;
scripts: JSX.Element;
}

type MyDocumentProps = DocumentProps & {
lang: string;
bodyClasses: string[];
faviconIrl: string;
arbitraryHeadStuff: JSX.Element;
};

const Document: Component<MyDocumentProps> = props => {
return (
<html lang={props.lang}>
<head>
<link rel="icon" href={props.faviconUrl} />
{props.assets}
{props.arbitraryHeadStuff}
</head>
<body class={props.bodyClasses}>
<div id="app">{props.children}</div>
{props.scripts}
...
</body>
</html>
);
};
type DocumentProps = {
assets: JSX.Element;
children: JSX.Element;
scripts: JSX.Element;
}

type MyDocumentProps = DocumentProps & {
lang: string;
bodyClasses: string[];
faviconIrl: string;
arbitraryHeadStuff: JSX.Element;
};

const Document: Component<MyDocumentProps> = props => {
return (
<html lang={props.lang}>
<head>
<link rel="icon" href={props.faviconUrl} />
{props.assets}
{props.arbitraryHeadStuff}
</head>
<body class={props.bodyClasses}>
<div id="app">{props.children}</div>
{props.scripts}
...
</body>
</html>
);
};
and then something like
export default createHandler<MyDocumentProps>(() => (
<StartServer document={props) => <Document {...props} />
));
export default createHandler<MyDocumentProps>(() => (
<StartServer document={props) => <Document {...props} />
));
ryansolid
ryansolidā€¢10mo ago
Yeah.. which is always hard with streaming as the head has to go first. Solid Meta does do that for MetaTags/Title but we don't have anything for augmenting tags it wouldn't create. It's a bit tricker. Old SolidStart actually handled this incidentally because it wrapped HTML and Body but it basically was impossible to get SSR: false mode working with it. The biggest challenge for how things are currently is that the Document component is never sent to the browser It is not part of the client bundle But I wonder if there is a way to be smarter about this at bundling time. Now that the lines are drawn so to speak. That would only be the first step. We still don't hydrate the head (had issues with browser extensions). But the HTML/Body tag would work.
andrezero
andrezeroOPā€¢10mo ago
Got it (I hope). I wasn't familiar with the streaming architecture / flow. It does not render app first and then the document When streaming it sends the document first and then streams updates to assets, scripts and children And there is (yet) no way to stream updates to the <html> and <body> tags. šŸ˜¬ Did I get it right?
ryansolid
ryansolidā€¢10mo ago
Yeah. Mostly. We can update things up to the first flush so it is often better to wait to start streaming until we've done a first pass, loaded critical data, but we've already rendered stuff at that point and we have special mechanisms to collect assets at that time. Anything misssed when it starts streaming today, gets added client side after the fact. We have plans to improve streaming of assets but updating <html> and <body> would need to be special cased as well. And I admit I don't have a plan for that currently.
Want results from more Discord servers?
Add your server