-
-
Couldn't load subscription status.
- Fork 1.8k
Importing the CSS for remote components #714
-
Min reproducible repo:
https://github.com/suhanw/learn-wp5-modfed-ssr
Background
I'm using CSS modules in my remote repo.
image
But I can't figure out a way to retrieve the CSS code for the remote component on server-side rendering. At the moment, while the HTML markup of the remote component is rendered correctly in SSR, the CSS link tag is only inserted into the DOM by remoteEntry.js on the client-side, resulting in a Flash Of Unstyled Content (FOUC) on page load:
ezgif-4-87053a6f1ae5
It appears that the only "workaround" for the remote component's CSS to be SSR-ed correctly is to use inline style (aka using the HTML style attribute):
image
Question:
I really want to refrain from using inline styles, is there a way to SSR remote components while still using CSS Modules in the remote repo?
Otherwise, if using inline style is unavoidable, is anyone aware of any webpack plugin that converts CSS modules into inline styles during transpilation? Hence allowing me to continue using the CSS modules convention in my codebase? 🤞🏻
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 22
We successfully used https://twind.style/ + Next.js. It requires fewer changes and is more or less predictable/stable
We had light/dark theme + accent colors so far, so good
Replies: 4 comments 20 replies
-
I have the same problem. Did you come up with a solution?
Beta Was this translation helpful? Give feedback.
All reactions
-
i don't have a solution yet :/
Beta Was this translation helpful? Give feedback.
All reactions
-
What I've done is to use mini-css-extract-plugin to extract all styles for the remote into a single .css file that I then include in the host app's HTML payload. This can be included manually, but in my case, I also expose a module from the remote that embeds the remote's own stylesheet so that the host doesn't have to be aware of these details.
This works, but isn't an ideal solution as it doesn't do any code splitting, so I'm on the hunt for something better too. For projects using loadable-components, I think this issue is relevant, though I'm using NextJS myself. For NextJS, I think this is solved with a Fetch-based solution (accessible via paid workshop).
Beta Was this translation helpful? Give feedback.
All reactions
-
This is a drawback of sharing modules, bigger chunks potentially. But this is also a problem of intrinsic dependency/coupling - technically the remote isn't fully self sustaining. Webpack doesn't know it's missing something - like if you had a react component use context but the host didn't implement it. Component is dependent on the host to have done something. Global css is a similar challenge, since nothing described in the exposed file that said anything about depending on X / it's not accessible during build since we don't know about remotes at build
Beta Was this translation helpful? Give feedback.
All reactions
-
So, interestingly enough, I don't think I am, unless tailwind does this automatically when injecting styles via the config, I'm going to try implementing the css in the tailwind css file itself and see if that makes a difference, could be it, will also be happy to message you on twitter momentarily.
Beta Was this translation helpful? Give feedback.
All reactions
-
This is a drawback of sharing modules, bigger chunks potentially. But this is also a problem of intrinsic dependency/coupling - technically the remote isn't fully self sustaining. Webpack doesn't know it's missing something - like if you had a react component use context but the host didn't implement it. Component is dependent on the host to have done something. Global css is a similar challenge, since nothing described in the exposed file that said anything about depending on X / it's not accessible during build since we don't know about remotes at build
This makes sense as the host styles is not able to be called via the remote in another server and the global styles don't inject into the component themselves, but actually just the host, so just explicitly call it instead.
Resolved via import of tailwind module in component, didn't have to adjust config just added:
import 'tailwindcss/tailwind.css in the component versus trying to add global styles to the app.
so:
import 'tailwindcss/tailwind.css'
const comp = () => (
// do tailwind stuff here
)
return comp;
Beta Was this translation helpful? Give feedback.
All reactions
-
Yeah exactly. It's a side effect some other file is adding. Same issue if you put context provider in a parent.
Tailwind auto teee shakes. So you need to opt out of that if you want to implicitly depend
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi, looking at the answer from @integrate-your-mind and @ScriptedAlchemy, I got a similar approach working in Angular and Nx, this may insterest @Coly010.
Suppose we have a host app loading from a federated components project. Both apps have Tailwind mode: 'jit' enabled.
For each component that needs to be exported, adding
@import 'tailwindcss/tailwind.css';
at the top the component's scss file works.
Of course, there is some duplication, it's not totally optimal. But this is a realistic scenario. The full Tailwind stylesheet for the components mfe is 31kB, loading ~5 components from the project would be ~170kB if all are used. That compared to the 3MB full Tailwind size without optimization seems like a good tradeoff. For the Angular case, I am thinking this could be further optimized by creating an "empty" styles component without ViewEncapsulation, importing as above. Consumers could import it first to load the optimal 34Kb styles once.
I have also contemplated a solution involving Tailwind safelist option, but I'm not so sure how that would exactly work.
Anyway, just some thoughts. This is an interesting problem in general
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 7
-
Key here is you need to flush the chunks out or include them. How I do it is I expose a module at the end of the cycle that adds all the chunk mappings for my remotes. So when I load a federated module, it can look up the styles that should be SSRd as well.
You could write a simple loader that exports the css chunk required. So you could retrieve the css files. Also check out federated stats plugin on npm.
I'd be happy to work with someone on this, like I've solved this directly built into next.js, I know what to do more or less, but don't have the time or resources to "just whip one out" - timeframe wise, with some help this could be solved in a few days.
So if someone wants to help, I can write out most the hooks or basic boilerplate with some references to other plugins where I'm doing the same exact thing.
Most of this is just a case of logging and a way to flush these on demand back to the host app.
Beta Was this translation helpful? Give feedback.
All reactions
-
If this is just browser side problems then it's also an easy fix. I think - it should just mean we need to tell webpack it should hold the promise and load additional chunks.
Beta Was this translation helpful? Give feedback.
All reactions
-
Very interesting topic.
I used the federated stats plugin to get the chunk mappings in a Node.js app (not Next.js), and I made it work for SSR (add stylesheets, preload assets, etc.).
But keeping chunk mappings in sync with the loaded module can be a bit challenging.
First, the federated module should expose this mapping somehow (it can be just a file in a CDN). Then, at some point, the server should fetch the mappings (at bootstrap time, on render, etc.). And finally, use a revalidation strategy for your federated modules and fetch chunk mappings for those that changed.
This process requires some tuning and adds some extra network calls.
I agree with this approach as it is more efficient and less error-prone:
...I expose a module at the end of the cycle that adds all the chunk mappings for my remotes. So when I load a federated module, it can look up the styles that should be SSRd as well.
But I'm not 100% sure how to do it. So @ScriptedAlchemy, could you please share those references you mentioned above?
I understand the mechanism to track and expose chunks could be framework agnostic and maybe be part of module-federation/node or another package. Is something related to this on the roadmap?
Beta Was this translation helpful? Give feedback.
All reactions
-
Well, I realized that in the case of SSR, exposing chunk mappings is not as straightforward as I thought.
When building the federated module for SSR, you run Webpack for browser and server targets, so you could get two different sets of chunks. Then, in the SSR phase, you use the federated module compiled for the server to render, but the chunks you want to know are the ones for the federated module compiled for the browser.
I think is feasible to add the browser chunk mapping to the federated module for the server, but it adds some constraints to the build as the server build will depend on the client build to get the stats and use it for the chunk mapping. So maybe there is a better way.
Beta Was this translation helpful? Give feedback.
All reactions
-
Server build already depends on client build at least in next apps. So this is a commons case.
You could also inject the manifest into the remote at the top after the builds both run.
Nextjs-SSR has my old flushing mechanism. It needs to be rewritten and implemented into nextmf. That's the last remaining feature.
Outside of next, it's a similar process but I'd have to make it be a little sdk or api so devs could wire it to whatever tool they use.
@apehead dm me on twitter- if you're interested I'd love some extra minds thinking about this. I can make it work but I'm looking for better implementations and my correlation algorithms are a little flaky.
One idea I had was during chunk load on server I could push the browser chunk id into a Map then flush it out at the end of the render.
I'd need to track the route since chunks don't load ever time you execute.
Or I could put a generator around the get think of MF api. So whenever the factory is called I push the id into Map
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
Is there a recommended way to bring styles (Tailwind) from a remote app (external UI package library) into a host app using NextJS? The only way I can get styles working between the two is by modifying the tailwind config to read the whole repo. This feels like quite a hacky method to bring in styles from one place to another.
Beta Was this translation helpful? Give feedback.
All reactions
-
Upon further review, I believe it's related to Tailwind's tree shaking not being able to access the logic from the UI Library so classes aren't being generated.
Beta Was this translation helpful? Give feedback.
All reactions
-
any solution for tailwind and nextjs?
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 2
-
For Tailwind, use the allowlist with a wide regex so it doesn't remove styles. Essentially, disable tree-shaking of Tailwind.
Beta Was this translation helpful? Give feedback.
All reactions
-
We successfully used https://twind.style/ + Next.js. It requires fewer changes and is more or less predictable/stable
We had light/dark theme + accent colors so far, so good
Beta Was this translation helpful? Give feedback.