Possibly unexpected behavior of extractCss() on SSR
See original GitHub issueHi, very nice library you have here! I am currently exploring it more and hoping to be able to use it as my go-to css-in-js solution in the future. Currently, I am finding issues with how the SSR works.
Goober seems to be using a single JS object during SSR to store all the generated styles. The store would be flushed when extractCss()
is called. https://github.com/cristianbote/goober/blob/master/src/core/get-sheet.js#L2
When doing SSR, this works fine if the server only handle 1 request at a time. Unfortunately, this approach doesn’t play well when we introduce some asynchronous tasks during SSR. When this happens, the order of css
and extractCss()
calls can vary depending on a lot of factors, basically a race condition.
An example to replicate this issue can be seen here: https://github.com/jackyef/preact-goober-snowpack/blob/master/server/index.tsx#L30
Output:
➜ preact-goober-snowpack git:(master) ✗ node .\server\dist\main.js
Koa server listening on port 8001
Welcome, SSR
Welcome, SSR
Welcome, SSR
{
style: " body{margin:0;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen','Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;} code{font-family:source-code-pro, Menlo, Monaco, Consolas, 'Courier New',monospace;}.go2238624862{text-align:center;}.go3990163120{background-color:#282c34;min-height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:calc(10px + 2vmin);color:white;}.go2358084271{height:40vmin;pointer-events:none;margin-bottom:1rem;animation:App-logo-spin infinite 1.6s ease-in-out alternate;}@keyframes App-logo-spin{ from{transform:scale(1);} to{transform:scale(1.06);}}.go2948822817{width:80%;}.go507845381{background:#333333;border-radius:2rem;border:1px solid #282c34;padding:2rem;display:block;width:30vw;margin:2rem auto;text-align:left;}@media only screen and (max-width: 600px){.go507845381 pre{padding:1.25rem;width:auto;}}"
}
{ style: '' }
{ style: '' }
Here, we can see that even when we sent 3 requests to the server, only the first request is getting all the extracted CSS rules. The asynchronous task effectively makes the order of operation to be like this:
- All
css
andglob
are called for the first request, then waits for the asynchronous task to finish. - All
css
andglob
are called for the first request, then waits for the asynchronous task to finish. - All
css
andglob
are called for the first request, then waits for the asynchronous task to finish. - Async task for first request finishes, run
extractCss()
. The single store for ssr css sheet is flushed. Send response back to first request with complete stylesheet. - Async task for second request finishes, run
extractCss()
, but the store is flushed already. Server sends response back to second request, with empty stylesheet. - Async task for third request finishes, run
extractCss()
, but the store is flushed already. Server sends response back to third request, with empty stylesheet.
Possible solution
Introduce a separate context to store stylesheet during SSR, so each request can have their own store. This might add more complexity for the project, but would allow SSR behavior to be better. Potential issue with this:
- Code performance -> we’ll need to come up with an efficient algorithm to do this
- Bundle size -> Ideally this shouldn’t affect the size of goober module used on client bundle. Adding the new SSR logic in its own separate package/file could be an option, like what react does with
react-dom/server
Issue Analytics
- State:
- Created 3 years ago
- Reactions:3
- Comments:18 (14 by maintainers)
Oooh, I see! Thank you for investigating. (And I realize this is a slightly different with original issue… Sorry to bother you. 😓 )
I’ve rewritten this style of codes
into this one.
And everything works fine. 🙌
Would you mind me sending PR for docs?
Please do! Much appreciate it! Thank you!