Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

conditional css in create-react-app

I have default css file and separate css file that should be applied (to owerride default) only when certain conditions are met.

I am using create-react-app wit default import 'file.css' syntax.

What is the best way forward to decide whether to load or not load particular css file dynamically?

like image 510
Martins Untals Avatar asked Oct 19 '17 17:10

Martins Untals


3 Answers

The require method only worked in development (as all the CSS is bundled upon build), and the import method did not work at all (using CRA version 3.3).

In our case, we have multiple themes, which cannot be bundled - so we solved this using React.lazy and React.Suspense.

We have the ThemeSelector, which loads the correct css conditionally.

import React from 'react';

/**
 * The theme components only imports it's theme CSS-file. These components are lazy
 * loaded, to enable "code splitting" (in order to avoid the themes being bundled together)
 */
const Theme1 = React.lazy(() => import('./Theme1'));
const Theme2 = React.lazy(() => import('./Theme2'));

const ThemeSelector: React.FC = ({ children }) => (
  <>
    {/* Conditionally render theme, based on the current client context */}
    <React.Suspense fallback={() => null}>
      {shouldRenderTheme1 && <Theme1 />}
      {shouldRenderTheme2 && <Theme2 />}
    </React.Suspense>
    {/* Render children immediately! */}
    {children}
  </>
);

export default ThemeSelector;

The Theme component's only job, is to import the correct css file:

import * as React from 'react';

// ๐Ÿ‘‡ Only important line - as this component should be lazy-loaded,
//    to enable code - splitting for this CSS.
import 'theme1.css';

const Theme1: React.FC = () => <></>;

export default Theme1;

The ThemeSelector should wrap the App component, in the src/index.tsx:

import React from 'react';
import ReactDOM from 'react-dom';
import ThemeSelector from 'themes/ThemeSelector';

ReactDOM.render(
  <ThemeSelector>
    <App />
  </ThemeSelector>,
  document.getElementById('root')
);

As I understand, this forces each Theme to be split into separate bundles (effectively also splitting CSS).


As mentioned in the comments, this solution does not present an easy way of switching themes runtime. This solution focuses on splitting themes into separate bundles.

If you already got themes split into separate CSS files, and you want to swap themes runtime, you might want to look at a solution using ReactHelmet (illustrated by @Alexander Ladonin's answer below)

like image 180
JAM Avatar answered Nov 02 '22 10:11

JAM


One simple solution that I found that works in production is to use vercel's styled-jsx. First, install styled-jsx:

npm install --save styled-jsx

Or if you use Yarn:

yarn add styled-jsx

Now create strings from your css file, so for instance:

const style1 = `
div {
    display: flex;
    flex-direction: column;
    align-items: center;
}
`
const style2 = `
div {
    display: flex;
    flex-direction: column;
    align-items: center;
}
`

And then in your React Component, you can do something like this:

const MyComponent = () => {
  return (
    <div className='my-component'>
      <style jsx>
        {
          conditionA ? style1: style2
        }
      </style>
    </div>
  )
}

Simply add <style jsx>{your_css_string}</style> to the component which you wish to add styling to and you can then to implement conditions just use different strings to import different css styling.

like image 23
Anh Nguyen Quy Avatar answered Nov 02 '22 12:11

Anh Nguyen Quy


You can use require('file.css') syntax instead. This will allow you to put it inside of a conditional.

e.g.

if(someCondition) {
    require('file.css');
}
like image 39
mpen Avatar answered Nov 02 '22 11:11

mpen