0
# JSX Provider-Based Theming
1
2
Theme switching using JSX provider components and theme objects. This approach is designed for component libraries like styled-components, emotion, Material-UI, and other CSS-in-JS solutions that use theme providers.
3
4
## Capabilities
5
6
### withThemeFromJSXProvider
7
8
Creates a decorator that wraps stories with a theme provider component and passes theme objects to the provider.
9
10
```typescript { .api }
11
/**
12
* Creates a decorator for JSX provider-based theme switching
13
* @param config - Configuration object specifying provider, themes, and options
14
* @returns Storybook decorator function
15
*/
16
function withThemeFromJSXProvider<TRenderer extends Renderer = any>(
17
config: ProviderStrategyConfiguration
18
): DecoratorFunction<TRenderer>;
19
20
interface ProviderStrategyConfiguration {
21
/** JSX provider component that accepts a theme prop */
22
Provider?: any;
23
/** Global styles component to render alongside the provider */
24
GlobalStyles?: any;
25
/** Name of the default theme */
26
defaultTheme?: string;
27
/** Mapping of theme names to theme objects */
28
themes?: Record<string, any>;
29
}
30
```
31
32
**Usage Examples:**
33
34
```typescript
35
import { withThemeFromJSXProvider } from '@storybook/addon-themes';
36
import { ThemeProvider } from 'styled-components';
37
import { lightTheme, darkTheme } from './themes';
38
39
// Styled-components example
40
export const decorators = [
41
withThemeFromJSXProvider({
42
themes: {
43
light: lightTheme,
44
dark: darkTheme,
45
},
46
defaultTheme: 'light',
47
Provider: ThemeProvider,
48
}),
49
];
50
51
// Material-UI example
52
import { ThemeProvider, createTheme } from '@mui/material/styles';
53
import CssBaseline from '@mui/material/CssBaseline';
54
55
const lightTheme = createTheme({
56
palette: {
57
mode: 'light',
58
},
59
});
60
61
const darkTheme = createTheme({
62
palette: {
63
mode: 'dark',
64
},
65
});
66
67
export const decorators = [
68
withThemeFromJSXProvider({
69
themes: {
70
light: lightTheme,
71
dark: darkTheme,
72
},
73
defaultTheme: 'light',
74
Provider: ThemeProvider,
75
GlobalStyles: CssBaseline,
76
}),
77
];
78
79
// Emotion example
80
import { ThemeProvider } from '@emotion/react';
81
import { Global, css } from '@emotion/react';
82
83
const GlobalStyles = () => (
84
<Global
85
styles={css`
86
body {
87
margin: 0;
88
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
89
}
90
`}
91
/>
92
);
93
94
export const decorators = [
95
withThemeFromJSXProvider({
96
themes: {
97
brand: { primary: '#007bff', secondary: '#6c757d' },
98
corporate: { primary: '#28a745', secondary: '#17a2b8' },
99
},
100
defaultTheme: 'brand',
101
Provider: ThemeProvider,
102
GlobalStyles: GlobalStyles,
103
}),
104
];
105
```
106
107
## Implementation Details
108
109
### Provider Wrapping
110
111
The decorator automatically:
112
- Wraps each story with the specified Provider component
113
- Passes the selected theme object as the `theme` prop
114
- Renders GlobalStyles component alongside the provider
115
- Handles theme switching by passing different theme objects
116
117
### Theme Object Structure
118
119
Theme objects can have any structure that your styling library expects:
120
121
```typescript
122
// Styled-components theme example
123
const lightTheme = {
124
colors: {
125
primary: '#007bff',
126
secondary: '#6c757d',
127
background: '#ffffff',
128
text: '#212529',
129
},
130
spacing: {
131
small: '8px',
132
medium: '16px',
133
large: '24px',
134
},
135
breakpoints: {
136
mobile: '768px',
137
desktop: '1024px',
138
},
139
};
140
141
// Material-UI theme example (using createTheme)
142
const muiTheme = createTheme({
143
palette: {
144
mode: 'light',
145
primary: {
146
main: '#1976d2',
147
},
148
secondary: {
149
main: '#dc004e',
150
},
151
},
152
typography: {
153
fontFamily: 'Roboto, Arial, sans-serif',
154
},
155
});
156
```
157
158
### Single Theme Usage
159
160
When only one theme is provided, the decorator still wraps stories with the provider:
161
162
```typescript
163
export const decorators = [
164
withThemeFromJSXProvider({
165
themes: {
166
default: singleTheme,
167
},
168
Provider: ThemeProvider,
169
}),
170
];
171
```
172
173
### GlobalStyles Integration
174
175
The GlobalStyles component is rendered alongside the provider, perfect for CSS resets and global styles:
176
177
```typescript
178
// Styled-components GlobalStyle
179
import { createGlobalStyle } from 'styled-components';
180
181
const GlobalStyle = createGlobalStyle`
182
body {
183
margin: 0;
184
font-family: ${props => props.theme.fonts.primary};
185
background-color: ${props => props.theme.colors.background};
186
color: ${props => props.theme.colors.text};
187
}
188
`;
189
190
export const decorators = [
191
withThemeFromJSXProvider({
192
themes: { light: lightTheme, dark: darkTheme },
193
defaultTheme: 'light',
194
Provider: ThemeProvider,
195
GlobalStyles: GlobalStyle,
196
}),
197
];
198
```
199
200
## Framework Integration
201
202
### Styled-Components
203
204
```typescript
205
import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
206
207
const GlobalStyle = createGlobalStyle`
208
body {
209
background-color: ${props => props.theme.background};
210
color: ${props => props.theme.text};
211
}
212
`;
213
214
const themes = {
215
light: {
216
background: '#ffffff',
217
text: '#000000',
218
primary: '#007bff',
219
},
220
dark: {
221
background: '#121212',
222
text: '#ffffff',
223
primary: '#66aaff',
224
},
225
};
226
227
export const decorators = [
228
withThemeFromJSXProvider({
229
themes,
230
defaultTheme: 'light',
231
Provider: ThemeProvider,
232
GlobalStyles: GlobalStyle,
233
}),
234
];
235
```
236
237
### Emotion
238
239
```typescript
240
import { ThemeProvider } from '@emotion/react';
241
import { Global, css } from '@emotion/react';
242
243
const GlobalStyles = ({ theme }) => (
244
<Global
245
styles={css`
246
body {
247
background-color: ${theme.background};
248
color: ${theme.text};
249
}
250
`}
251
/>
252
);
253
254
export const decorators = [
255
withThemeFromJSXProvider({
256
themes: emotionThemes,
257
defaultTheme: 'light',
258
Provider: ThemeProvider,
259
GlobalStyles: GlobalStyles,
260
}),
261
];
262
```
263
264
### Material-UI (MUI)
265
266
```typescript
267
import { ThemeProvider, createTheme } from '@mui/material/styles';
268
import CssBaseline from '@mui/material/CssBaseline';
269
270
const lightTheme = createTheme({
271
palette: { mode: 'light' },
272
});
273
274
const darkTheme = createTheme({
275
palette: { mode: 'dark' },
276
});
277
278
export const decorators = [
279
withThemeFromJSXProvider({
280
themes: {
281
light: lightTheme,
282
dark: darkTheme,
283
},
284
defaultTheme: 'light',
285
Provider: ThemeProvider,
286
GlobalStyles: CssBaseline,
287
}),
288
];
289
```