Theming
Unified CSSinJS theming solution for React
ThemeProvider
allows you to pass, update, merge and augmenttheme
through context down react tree.withTheme
allows you to receive theme and its updates in your components as atheme
prop.createTheming
allows you to integratetheming
into your CSSinJS library with customchannel
(if you need custom one).
See Motivation for details.
Table of Contents
Install
npm install --save theming
# or
yarn add theming
Usage
In your components
Note: this component is used to show what theme you receive.
import React from 'react';
import { withTheme } from 'theming';
const DemoBox = props => {
console.log(props.theme);
return (<div />);
}
export default withTheme(DemoBox);
In your app
import React from 'react';
import { ThemeProvider } from 'theming';
import DemoBox from './components/DemoBox'
const theme = {
color: 'black',
background: 'white',
};
const App = () => (
<ThemeProvider theme={theme}>
<DemoBox /> {/* { color: 'black', background: 'white' } */}
</ThemeProvider>
)
export default App;
Playground demo
Be our guest, play with theming
in codesandbox:
https://codesandbox.io/s/jvwzkkxrp5
Motivation
These components are enabling seamless theming for your react applications. So as far as you don't want to pass the theme object to every component. That's why you want to use context. However, as far context feature is experimental API and it is likely to break in future releases of React you don't want to use it directly. Here theming
comes to play.
If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes.
If you insist on using context despite these warnings, try to isolate your use of context to a small area and avoid using the context API directly when possible so that it's easier to upgrade when the API changes. — Context, React documentation
Regarding isolation your use of context to a small area and small areas_ in particular our very own react prophet Dan Abramov have a thing to say:
Should I use React unstable “context” feature? — Dan Abramov @dan_abramov on Twitter
So you are okay to use context for theming. theming
package provides everything you need to do that:
- The
ThemeProvider
allows you to pass and update your theme through context down the react tree. withTheme
allows you to receive theme and its updates in your components as atheme
prop.createTheming
allows you to integratetheming
into your CSSinJS library with a customcontext
(if you need custom one).
API
ThemeProvider
React High-Order component, which passes theme object down the react tree by context.
import { ThemeProvider } from 'theming';
const theme = { /*…*/ };
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
props
props.theme
Required
Type: Object
, Function
If its Object
and its root ThemeProvider
then it's intact and being passed down the react tree.
const theme = { themed: true };
<ThemeProvider theme={theme}>
<DemoBox /> {/* { themed: true } */}
</ThemeProvider>
If its Object
and its nested ThemeProvider
then it is being merged with the theme from the parent ThemeProvider
and passed down to the react tree.
const theme = { themed: true };
const patch = { merged: true };
<ThemeProvider theme={theme}>
<ThemeProvider theme={patch}>
<DemoBox /> {/* { themed: true, merged: true } */}
</ThemeProvider>
</ThemeProvider>
If its Function
and its nested ThemeProvider
then it's being applied to the theme from parent ThemeProvider
. If the result is an Object
it's passed down to the react tree, throws otherwise.
const theme = { themed: true };
const augment = outerTheme =>
Object.assign({}, outerTheme, { augmented: true });
<ThemeProvider theme={theme}>
<ThemeProvider theme={augment}>
<DemoBox /> {/* { themed: true, augmented: true } */}
</ThemeProvider>
</ThemeProvider>
props.children
Required
Type: PropTypes.element
withTheme(component)
React High-Order component, which maps context to theme prop.
component
Required
Type: ComponentType
You need to have ThemeProvider
with a theme somewhere upper the react tree after that wrap your component in withTheme
and your component gets the theme as a prop. withTheme
handles the initial theme object as well as theme updates.
PS. It doesn't break if you have PureComponent
somewhere in between your ThemeProvider
and withTheme
.
Usage with Component:
import React from 'react';
import { withTheme } from 'theming';
const DemoBox = props => {
console.log(props.theme);
return (<div />);
}
export default withTheme(DemoBox);
In the app:
import React from 'react';
import { ThemeProvider } from 'theming';
import DemoBox from './components/DemoBox'
const theme = {
color: 'black',
background: 'white',
};
const App = () => (
<ThemeProvider theme={theme}>
<DemoBox /> {/* { color: 'black', background: 'white' } */}
</ThemeProvider>
)
export default App;
Access inner component instance
The withTheme
HOC supports the new React forwardRef API so you can use the regular ref prop.
useTheme
When you are on React 16.8 higher you will be able to use the useTheme
hook which will return the theme object.
Usage with Component:
import React from 'react';
import { useTheme } from 'theming';
const DemoBox = () => {
const theme = useTheme();
console.log(theme);
return (<div />);
}
export default Demobox;
createTheming(context)
Function to create ThemeProvider
and withTheme
with custom context.
The context you pass in is used.
context
Type: Object
Result: Object { withTheme, ThemeProvider, useTheme }
withTheme
, ThemeProvider
and useTheme
will use the context passed to createTheming
.
Note: You will only be able to use useTheme
when you are on React version 16.8 or higher.
import { createTheming } from 'theming';
import React from 'react';
const context = React.createContext({});
const theming = createTheming(context);
const { withTheme, ThemeProvider, useTheme } = theming;
export default {
withTheme,
ThemeProvider,
useTheme,
};
ThemeContext
We export the default ThemeContext so you can use it with the new static contextType
with classes or even the new React Hooks API.
This is the context which also the exported withTheme
and ThemeProvider
use.
import { ThemeContext } from 'theming';
Credits
- Thanks to jss creator Oleg Slobodskoi @kof for immersive help, support and code review.
- Thanks to styled-components creator Max Stoiber @mxstbr for initial and battle tested implementation of theming support in styled-components as well as help and code review.
- Thanks to styled-components' core team member Phil Plückthun @philpl for help and code review.
- Thanks to glamorous creator Kent C. Dodds @kentcdodds for help, support and code review.
- Thanks to glamorous's core team member Alessandro Arnodo @vesparny for improved theming support in glamorous and brcast.
- Thanks to Gert Sallaerts @gertt for the playground demo.
License
MIT © Vladimir Starkov