Generate static UI elements from server data only ONCE
I want to generate my navbar components based on my server folder structure
Specifically, I have folder
countries
which contains file us.json
and ca.json
. For each file in folder countries
, I want to generate an <A>
element onto the Layout component. I struggle to know how to "send" this data from server to client and construct the UI, but only ONCE at startup - since the countries
folder is static
Stackblitz: https://stackblitz.com/~/github.com/bhuynhdev/solid-start-test
My current code doesn't work. Locally, the page displays, but the Router just doesn't work - clicking on links only change the URL but doesn't update page content
15 Replies
I could make a
getLinks
query-wrapped function and expose the data via a createAsync
. But the cache would only be 5 minutes, thus getLinks
is bound to be called again. I want to explore a way to do this processing only absolutely ONE timeThis doesn't work as you're importing Node's
path
library on the client
The cache provided by query
is only per-request on the server and for like 15 seconds on the client, with this approach you'd probably want to add a cache-control
header
A halfway point would be to generate links
in a vite plugin and expose it via a virtual module. The array would be generated at build time, then it'd be used on the server to render Layout
each time, and would be shipped to the client as an immutably cached file.
Not quite the 'generate static UI elements from server data' but closer than using a server functionI am interested in the "cache-control" header solution. Do you know of a way to add cache-control header to query functions ?
Return a web
Response
from the server function that has the header
Either with new Response
or the json/redirect/reload
helpers from solid routerBut the cache would only be 5 minutesActually the cache is indefinite as long as there is at least one
createAsync
referencing it.
- 5 seconds is the time limit to between a route preload
and the first createAsync
to use the warmed query
. As long as there is at least one single active createAsync
, it is assumed that the client is responsible for invalidating the query
whenever necessary. It's only when the reference count drops to 0
that the query will rerun on the next access.
- 5 minutes is the time limit before an unreferenced cached value is deleted. This really only affects bfcache
; i.e. code that runs after “go back”.
https://discord.com/channels/722131463138705510/1336421521232494654/1336754105791676466Oh I see. Since this
getLinks
createAsync would live in the root layout (and the NavBar is always present on every page), there will always be at least one createAsync
referencing the cache. And since arguments to the getLinks
function won't change, this cache will be indefinite without needing to use cache-control header.
I assume this indefinite cache lives on the server memory, not the browser cache ?GitHub
solid-router/src/data/query.ts at 50c5d7bdef6acc5910c6eb35ba6a24b15...
A universal router for Solid inspired by Ember and React Router - solidjs/solid-router
(and the NavBar is always present on every page)In many applications the
Not Found
page lives outside of the rest of the app tree. In that case one navigation to Not Found
is enough for the count to drop to 0
.Thanks a lot. That will work enough for me for now. The frequency of accessing "Not Found" is rare so I can take this trade-off
Overall, it seems like there is currently no way for the SSR to grab server data and directly serializes the data into the markup one-time as truly static HTML strings.
The benefit of cache-control is that it is stored in the browser cache but to use it effectively the fetches need to appear identical to the browser.
At that point it may make sense to create an API Route to make it easy to control the "shape of the fetch" from the client (for the benefit of the browser).
Once the response is properly cached and it's ensured that every fetch from the
query
looks identical from the browser's point of view even if the query
is rerun the cached response is used.MDN Web Docs
Cache-Control - HTTP | MDN
The HTTP Cache-Control header holds directives (instructions) in both requests and responses that control caching in browsers and shared caches (e.g., Proxies, CDNs).
no way for the SSR to grab server data and directly serializes into the markup one-time as truly static HTML strings.To take advantage of the hydration process you would have to cache the map (by country) data at the application root inside a context. Basically the Provider could manage a store that gets filled with country data as needed. I suspect that whatever country data was used for SSR would end up in the hydration data to initialize the store; though I think an experiment to verify this would be advisable as details of how the store is used may impact whether it gets hydrated or not.
So if I understand this correctly, you meant serializing the data not into the markup but into a ContextProvider ?
Basically I'm trying to piggyback on the hydration of the reactive graph.
Thanks. I will probably not try to mess with the hydration of the reactive graph. I slowly have come to accept the realization that, maybe I the way I wanted to use Solid Start for my scenario was not correct/was not its intended use case, and if my data is very static, maybe I should just generate a JSON file once on server start, then import it for re-use
I wonder if React Server Component could "solve" my use case: generating the actual HTML content once on the server, then just sends the static HTML string back --> no data fetching is needed
Nope.
RSCs don't create HTML; that's a common over simplification; they create
ReactNode
s. Because RSCs can ship props to client side components ReactNode
s + HTML are sent to the client; the only thing you are saving is the JS that generates the ReactNode
s.
Pure HTML requires Server Islands/Lakes ((client )islands are typically associated with MPA routing, i.e. route navigations are always server rendered).