React Styled Components stripped out from production build - reactjs

I use Styled Components as CSS alternative for my React App. In development everything works fine (first screenshot), but when I run a production build (npm build), styles within style tags are stripped out (second screenshot). As a result, there're no styles in the production build.
Here is the production version: http://projects.loratadin.com.s3-website-us-east-1.amazonaws.com/weather-app/
Here is the source code: https://github.com/Loratadin/weather-app

I had a similar issue with empty style tags in production. I'm using a headless browser for server-side rendering and this issue caused the server-side rendered pages to appear with no styles before JS assets are loaded.
After a lot of searching around, I finally found out the reason. The Styled Components library uses something called the "Speedy mode" to inject styles on production. This makes the styles bypass the DOM` and be injected directly inside the CSSOM, thus, appearing in the inspector, but totally invisible on the DOM.
Fortunately, Styled Components 4.1.0 came with a fix for this issue! Now you can set a global variable called SC_DISABLE_SPEEDY to true in order to disable the Speedy mode and get the styles to appear on Production as well. Keep in mind that you should do it at the very beginning of your application's entry file, before importing any styled component, otherwise it will not work.
The way I did it is by creating a new file called globals.js that contains global.SC_DISABLE_SPEEDY = true
and importing it as the very first thing in my index.js.
Reference: https://www.styled-components.com/releases#v4.1.0

For the Create React App folks out there you can add a .env file in your root and add:
REACT_APP_SC_DISABLE_SPEEDY=true

I was able to replicate your issue and it looks like when the application is in production, it can't select html elements within a styled component (the styles don't apply to the element). Instead, you'll need to create additional stylized components for input and button.
Working example: https://github.com/mattcarlotta/Weather-App
I refactored your application to simplify its structure. Please read the README for instructions on how to run it in development and in production (DO NOT use the above repository for production, as it's highly unnecessary to have an express backend -- I only did this so that you can run a local production build).
What I changed:
Moved any styled components to the components folder for modularity
Any sort of global stylization was put into a styles folder
Moved assets to images and imported them into the styled component that needed them (eliminating the need to use require('../path/to/image'))
Simplified the App.js file. Children are controlled by state and class methods. Most importantly, turned your form into a controlled component, fixed the getWeather method to: Not allow an empty submission, if the AJAX calls fails, it'll catch the error (instead of breaking your app), and reset the form inputs on successful submission.
Added prop-types to ensure props were consistent in declaration (string remains a string, number remains a number, and so on).

Related

When using react lazy-loading what magic creates the vendor bundles

I have started to configure lazy-loading for our application.
We have react 18 and webpack 5.
I started with the routes as that seems to be the place to start.
But after doing this I noticed that there are several bundles auto created.
For example we have a component library and several of them are auto created as a bundle when navigating to a route, it works but dont understand how this automagic works.
One thing is breaking the page though. We have a page where we show some charts.
When I navigate there I can see that this file is loaded:
vendors-node_modules_chart_js_dist_chart_mjs.bundle.js
But I have not specified anywhere to create this vendor bundle and this dont contain the correct code.
The import looks like this:
import { Chart, Tooltip, BarController, BarElement } from "chart.js";
To make the chart-page work I need to navigate to a completly other route that downloads a onther vendor-bundle containing the correct chart.js code. And heres the kicker.. that page dont use any charts at all...
This leads to my question, how and where to I configure this automatic bundle creation and why does this happen?

Use single StylesContext when importing component from custom MUI-based library

I have an application (let's call it frontend) importing a custom library of React components (design-system) that's Material UI based.
In the frontend app, I have a ShadowDOM-contained component (using react-shadow) that I want to import a Button component from design-system to. I wrapped the whole tree inside ShadowDOM with StylesProvider that I passed a custom instance of jss to (with different injectionPoint, so the styles from inside that tree are attached not at the head of the document, but inside the ShadowDOM.
The issue I have is, that frontend uses its own copy of StylesProvider from #material-ui/styles package (I have no need to install the whole #material-ui/core as all components should be imported from design-system instead) and design-system uses its own copy of StylesProvider from #material-ui/core, resulting in a situation, where the Button is being wrapped with another StylesProvider by MUI because it doesn't detect any styles context available (and it doesn't, because MUI creates the context in global scope of StylesProvider.js file - so there are two different contexts that do the same), resulting in Button having access to default JSS instance where the insertionPoint is pointing to document head.
What I'd like to achieve is, have MUI not create another styles context for every single component imported from design-system but use the one I defined in frontend - without passing it as a prop manually to every component imported. I considered adding #material-ui/styles on top of #material-ui/core to design-system and adding it to webpack's "externals" (so, theoretically, only one StylesProvider.js would be used and in turn - one context), but I was hoping there's a better solution.
TLDR:
Just adding #material-ui/styles to externals in design-system's webpack config was enough (even if you don't add it explicitly as dependency, #material-ui/core depends on it and will use it anyway, so it works).
Long answer:
My setup is such that the "main" entrypoint in package.json of design-system points to dist/index.js (so, already bundled/minified version of the library). Without adding #material-ui/styles to "externals" in webpack config, it was bundled along the whole library, so at this point, whatever I did there would always be two copies of StylesProvider.js:
One solution is to mark #material-ui/styles as external - this way, it won't be bundled in dist/index.js and instead, expected to be available during runtime (and it is):
Besides the aforementioned solution, there's also another one that worked - pointing "main" entry in package.json to "src/index.ts" instead. It made the frontend webpack treat it as a part of frontend code, instead of separate library:
As a bonus, it ended up with the final bundle being smaller (as common dependencies weren't duplicated). One minus of this approach was, however, that each frontend build had to also rebuild design-system, which increased the total build time.

How to get Emotion/Rollup/Typescript component CSS to show up in consuming React app?

I'm using TypeScript/Emotion/Rollup/Storybook to build a component library meant to be consumed by a React app. A couple preface points:
The consuming React app is working fine, and I can create Emotion-styled components within that app, and it works great and all CSS styles are applied to the app's own components.
The component library is working fine, and I can create Emotion-styled components, open Storybook, and see all the CSS styles applied in the lib's components in Storybook.
However, when I bring components over from my component library into my consuming app, their styles disappear, and upon inspection, the element looks like this:
<button data-testid="Button" css="You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop).">Submit</button>
In other words, the CSS of the component, as created in the component library, does not show up. This same element works fine in the lib's Storybook.
As an additional note, I saw the same error originally in the consuming app's OWN components, and solved it using Babel presets here as described here: https://emotion.sh/docs/css-prop
I stumbled across this question/answer, which led me to believe that the error I'm now experiencing happened because Rollup wasn't running the build through Babel: https://spectrum.chat/emotion/help/solved-css-prop-styles-not-appearing-in-built-components~cb6e75b8-7356-42d0-860b-bb01a26a3d06
After some trial and error, I got the component lib to build using Babel. But the error persists as always, and the component, when used inside the consuming app, remains unstyled with that note inside of the CSS prop.
Any ideas on how I might get the component's CSS to show up in the consuming React app?
Thanks in advance for any help!
Make sure your .tsconfig.json has compilerOptions.target: 'es6' and compilerOptions.jsx: "preserve"
and your .babelrc should have:
"presets": [
"#babel/preset-react",
"#emotion/babel-preset-css-prop"
],

Issue on Product version of Styled-Components when render with Rendertron

have very simple sample app which build Create React App + Styled-Components to prove this issue. But I have real big application which I am facing this issue which I am going to explain it below.
I would like to pre-render this app with Rendertron for SEO/GoogleBots and etc. But the problem is when I build PRODUCTION version of React App which use Styled-Components . all the style will be missing on static version which Rendertron produced, but from other side if I try the same workflow with dev-server of app , everything looks fine .
So far I know there is different on PROD version and DEV version of my application when I render it with Rendertron . But I am not sure what cause this issue and how I can fix this issue .
I am looking for solution or idea which can help me to solve this issue .
Here is my sample code which I peppered for test .
https://github.com/AJ-7885/test-styled-component-with-rendertron
Here is screen shot from different version of Rendered version by Rendertron base on PROD or DEV version of the same application .
enter image description here
After a lot of searching around, I finally found out the reason. The Styled Components library uses something called the "Speedy mode" to inject styles on production. This makes the styles bypass the DOM` and be injected directly inside the CSSOM, thus, appearing in the inspector, but totally invisible on the DOM.
Fortunately, Styled Components 4.1.0 came with a fix for this issue! Now you can set a global variable called SC_DISABLE_SPEEDY to true in order to disable the Speedy mode and get the styles to appear on Production as well.
Reference: https://www.styled-components.com/releases#v4.1.0
But the only part I am not sure , how to set disable this Speedy Mode in Create-React-App without Ejecting , Dose any body has any idea ?
You need to render your styles on the server side and inject those styles in your pre-rendered react app. Styled-components explains how to do that here: https://www.styled-components.com/docs/advanced#server-side-rendering
Also, I'd recommend using react-snap for pre-rendering since that is recommended by the Create React App docs. react-snap seems to be more of a React-specific solution that may be easier to implement, especially with styled-components.

CSS Reset with PostCSS autoreset

I'm trying out postCSS on a project using the create-react-app starter repo with postCSS and importing .css files for each component. That is, each component includes a import 'styles/componentA.css';, or several.
In order to create consistent styles across browsers I'd like to import a CSS reset. I've tried a few things:
Importing a .css file containing a standard CSS Reset based on Eric Meyer's reset.
Using the autoreset plugin for postCSS.
For the first option , in dev mode the CSS imports are added as <style> tags to the document. The CSS Reset appears last in the list of <style> tags, though it's imported in the top-level component. Ideally the CSS Reset would be imported first. Being last means it would overwrite any styles I apply to base elements (like h1 {font-size: 40px;})
For the 2nd option (using autoreset), it doesn't appear there's a way to apply specific rules to specific elements. For example, I want to apply list-style: none only to ul and ol elements.
Is it possible to use the autoreset plugin to do a Eric Meyer-like CSS Reset in postCSS? Or am I going in a completely wrong direction and misunderstanding the purpose of it?
I don't use react but I faced a similar issue when using components and resets alongside PostCSS (I'm building things as described here: http://ecss.io/chapter7.html). I didn't want a reset loaded for each component, just a global one as would be used traditionally.
Therefore, I went with a 'component' purely for 'globalCSS'. This is the first component that gets loaded and loads in the reset (and also global PostCSS variables and mixins) so subsequent components have access to them without redeclaring.
Is that an approach you could use/adapt?

Resources