question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Possibly unexpected behavior of extractCss() on SSR

See original GitHub issue

Hi, 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:

  1. All css and glob are called for the first request, then waits for the asynchronous task to finish.
  2. All css and glob are called for the first request, then waits for the asynchronous task to finish.
  3. All css and glob are called for the first request, then waits for the asynchronous task to finish.
  4. 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.
  5. Async task for second request finishes, run extractCss(), but the store is flushed already. Server sends response back to second request, with empty stylesheet.
  6. 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:

  1. Code performance -> we’ll need to come up with an efficient algorithm to do this
  2. 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:closed
  • Created 3 years ago
  • Reactions:3
  • Comments:18 (14 by maintainers)

github_iconTop GitHub Comments

2reactions
leader22commented, Nov 13, 2020

So what happens is that the css call is defined at code runtime

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

const Component = () => <div class={style} />;
const style = css({});

into this one.

const Component = () => <div class={style()} />;
const style = () => css({});

And everything works fine. 🙌

Maybe I should add this note on the css section

Would you mind me sending PR for docs?

1reaction
cristianbotecommented, Nov 13, 2020

Please do! Much appreciate it! Thank you!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Developers - Possibly unexpected behavior of extractCss() on SSR -
Hi, very nice library you have here! I am currently exploring it more and hoping to be able to use it as my...
Read more >
Eliminating Unused CSS - SurviveJS
Eliminating unused CSS is possible using PurgeCSS. It performs static analysis against the source. The functionality can be enabled through purgecss-webpack- ...
Read more >
@studiobear/designspek NPM | npm.io
Main detractor of stability is the course of finding an SSR best-practice for ... extractCSS() is called. active param is experiment for using...
Read more >
Why I Write CSS in JavaScript | Hacker News
You have to carefully name your variables using complicated conventions in order to avoid any conflicts or unexpected behavior.
Read more >
Dec 03 2019 14:12 UTC - angular/angular - Gitter
It's how the WAR is packaged that's causing this behavior. Natalia. @petajamaja ... However, my Input() and Output() is not working.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found