How can I create a ThemeProvider that adds a global css file? - css

How would you add a global css file as a custom ThemeProvider component in React? I'm building a component library for NPM. So it would enable another React application to use the css file globally.
I've tried creating one like this:
import React from 'react';
import './global.css';
interface Types {
children: React.ReactNode
}
export default (props:Types) => <div>{props.children}</div>

I found a neat solution. It creates a <style /> tag with the contents of a css file:
import React from 'react';
import styles from './global.css'
interface Types {
children: React.ReactNode
}
export default (props:Types) => (
<>
<style dangerouslySetInnerHTML={{__html: styles.replace(' ', '')}} />
<div>{props.children}</div>
</>
)

Related

nextjs " Failed to compile pages/_app.js " how to fix it [duplicate]

This question already has answers here:
Next.js Global CSS cannot be imported from files other than your Custom <App>
(11 answers)
Closed 1 year ago.
How to fix it, it happens when compiling
This is the official document for your problem from the Next.js team:
https://nextjs.org/docs/basic-features/built-in-css-support
You need to import global CSS in the _app.js file to use it globally in your application.
For example, use Tailwind CSS as global like this:
// your _app.js file
// import your CSS file you want to use globally here
import 'tailwindcss/tailwind.css';
const CustomApp = ({ Component, pageProps }) => (
<Component {...pageProps} />
);
export default CustomApp;
If you just want to use CSS for specific components, please use module CSS. It will be helpful to code splitting your bundle files when you build the production version.
Example to use modular CSS just only for your component:
import styles from './index.module.scss';
const YourComponent = () => {
return <div className={styles.example}>...</div>;
};
export default YourComponent;
For special cases that you need to import CSS files from the library inside your node_modules. You can import that CSS file from node_modules inside your specific component.
For example:
import { FC } from 'react';
import Slider from 'react-slick';
import 'react-slick/css/slick.css';
import 'react-slick/css/slick-theme.css';
const SlickSlider: FC<any> = ({ children, ...restProps }) => {
return <Slider {...restProps}>{children}</Slider>;
};
export default SlickSlider;

Separate Css files in typescript react project do not apply

My problem is the following, I created a new react project with typescript. And added a custom component that has a separate css file for it's styling. The folder structure is like this:
In the Header.css I defined a class:
.mainHeading {
color: green;
}
And referenced it in the Header.tsx like this:
import React from "react";
import styles from './Header.css';
function Header() {
return(
<h1 className={styles.mainHeading}>Streamfiuse</h1>
);
}
export default Header;
To do this I added the following to the react-app-env.d.ts
declare module '*.css';
I'm using the Header component in the App.tsx like the following
import React from 'react';
import Discover from './components/discover/Discover';
import Header from "./components/header/Header";
import './App.css';
function App() {
return (
<div className="App">
<Header />
<Discover />
</div>
);
}
export default App;
The problem is now that I would expect the heading "Streamfiuse" to appear in green, but apparently it doesn't. I'm new to react so any help is appreciated.
Edit 1
I also tried this:
import React from "react";
import './Header.css';
function Header() {
return(
<h1 className="mainHeading">Streamfiuse</h1>
);
}
export default Header;
But does't work either.
Try importing like this:
import './Header.css'
And applying the mainHeading class as a string
Maybe you could try this
Change import styles from './Header.css'; into import './Header.css';
Change className={styles.mainHeading} into className="mainHeading"
import React from "react";
import './Header.css';
function Header() {
return(
<h1 className="mainHeading">Streamfiuse</h1>
);
}
export default Header;

How to add a global decorator in Storybook

In ReactJs project you can use .storybook/preview.js file to add global decorators and parameters. How to achieve this same behaviour with #storybook/react-native?
What I need is to wrap all my stories with ThemeProvider but the unique way that I found is to wrap individual stories with .addDecorator().
Edit storybook/index.js, by using addDecorator on it.
Example:
import React from 'react'
import { getStorybookUI, configure, addDecorator } from '#storybook/react-native'
import Decorator from './Decorator'
addDecorator(storyFn => (
<Decorator>
{storyFn()}
</Decorator>
))
// import stories
configure(() => {
require('../stories')
}, module)
const StorybookUI = getStorybookUI({ onDeviceUI: true })
export default StorybookUI;;
Found an updated answer in Storybook's own documentation.
// .storybook/preview.js
import React from 'react';
export const decorators = [
(Story) => (
<div style={{ margin: '3em' }}>
<Story />
</div>
),
];
As of June 2021, using storybook v5.3.25, the above answer does not work. However I have managed to figure out a solution.
Decorators must be added to the storybook/index.js file in the following format:
import { ThemeDecorator } from './storybook/ThemeDecorator';
addDecorator(withKnobs); // inbuilt storybook addon decorator
addDecorator(ThemeDecorator);// custom decorator
configure(() => {
loadStories();
}, module);
in this instance, ThemeDecorator.js is a simple wrapper component that renders your story, it would look something like this:
import React from 'react';
import { Provider } from 'theme-provider';
export const ThemeDecorator = (getStory) => (
<Provider>{getStory()}</Provider>
);
Importantly, the addDecorator function expects a React component (not a wrapper function as other examples claim), that it will render, with its props being a reference to an individual story at runtime.

CSS file is applying on another react component even without importing

Hello I'm using React to build a website and I want to use .CSS files for my CSS and I'm using import './example.css' in my component file.
Example:
import React from 'react';
import 'Home.css';
const Home = () => {
return (
<div className="example">
Hi
</div>
)
}
and if i create another page but don't import this CSS file, I get the styles on my other page
other page:
import React from 'react';
const About= () => {
return (
<div className="example">
Hi
</div>
)
}
Any reason and solution for this?
When importing a css file like you've done it will be injected into the project and not just into the component you're importing it from.
What you're looking for is css-modules (adding css-modules using create-react-app)
import React from 'react';
import styles from 'Home.css';
const Home = () => {
return (
<div className={styles.example}>
Hi
</div>
)
}
The reason is that you are using the same class in both of your components.
Doing import 'Home.css' does not encapsulate .css only for that component, all of the .css gets bundled together so it ends up overwriting styles somewhere down the line.
For each of the components, you can specify a unique className on top of your component, and use that class to style only that component.
.home-container .header { ... }
You can also make one global .css part to put styles that you want to keep using throughout the whole app.

Dynamically load .css based on condition in reactJS application

I have a reactJS application that I want to make available to multiple clients. Each clients has unique color schemes. I need to be able to import the .css file that corresponds to the specific client.
For example, if client 1 logs into the application, I want to import client1.css. if client 2 logs into the application, I want to import client2.css. I will know the client number once I have validated the login information.
The application contains multiple .js files. Every .js file contains the following at the top of the file
import React from 'react';
import { Redirect } from 'react-router-dom';
import {mqRequest} from '../functions/commonFunctions.js';
import '../styles/app.css';
Is there a way to import .css files dynamically for this scenario as opposed to specifying the .css file in the above import statement?
Thank you
Easy - i've delt with similar before.
componentWillMount() {
if(this.props.css1 === true) {
require('style1.css');
} else {
require('style2.css');
}
}
Consider using a cssInJs solution. Popular libraries are: emotion and styled-components but there are others as well.
I generally recommend a cssInJs solution, but for what you are trying to do it is especially useful.
In Emotion for example they have a tool specifically build for this purpose - the contextTheme.
What cssInJs basically means is that instead of using different static css files, use all the power of Javascript, to generate the needed css rules from your javascript code.
A bit late to the party, I want to expand on #Harmenx answer.
require works in development environments only, once it goes to production you're likely to get errors or not see the css file at all. Here are some options if you, or others, encounter this:
Option 1: Using css modules, assign a variable of styles with the response from the import based on the condition.
let styles;
if(this.props.css1 === true) {
//require('style1.css');
import("./style1.module.css").then((res) => { styles = res;});
} else {
//require('style2.css');
import("./style2.module.css").then((res) => { styles = res;});
}
...
<div className={styles.divClass}>...</div>
...
Option 2: using Suspend and lazy load from react
// STEP 1: create components for each stylesheet
// styles1.js
import React from "react";
import "./styles1.css";
export const Style1Variables = (React.FC = () => <></>);
export default Style1Variables ;
// styles2.js
import React from "react";
import "./styles2.css";
export const Style2Variables = (React.FC = () => <></>);
export default Style2Variables ;
// STEP 2: setup your conditional rendering component
import React, {lazy, Suspense} from "react";
const Styles1= lazy(() => import("./styles1"));
const Styles2= lazy(() => import("./styles2"));
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
{isClient1() ? <Styles1 /> : <Styles2/>}
</Suspense>
{children}
</>
);
};
// STEP 3: Wrap your app
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
Option 3: Use React Helm which will include a link to the stylesheet in the header based on a conditional
class App extends Component {
render() {
<>
<Helmet>
<link
type="text/css"
rel="stylesheet"
href={isClient1() ? "./styles1.css" : "./styles2.css"}
/>
</Helmet>
...
</>
}
}
Personally, I like option 2 because you can set a variable whichClientIsThis() then modify the code to:
import React, {lazy, Suspense} from "react";
let clientID = whichClientIsThis();
const Styles= lazy(() => import("./`${clientID}`.css")); // change your variable filenames to match the client id.
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
<Styles />
</Suspense>
{children}
</>
);
};
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
This way you don't need any conditionals. I'd still recommend lazy loading and suspending so the app has time to get the id and make the "decision" on which stylesheet to bring in.

Resources