Theming
Jumbo React Next uses MUI v7 with Emotion for CSS-in-JS styling. The @jumbo framework
extends standard MUI theming with four independent region themes (main, header, sidebar, footer),
each supporting three modes (light, semi-dark, dark). Runtime theme switching is provided by the
CustomizerSettings drawer.
Theme regions
Splitting the theme into four independent regions lets each area of the layout carry its own color scheme. A common pattern is a dark sidebar with a light main content area:
| Region | Controls | Files |
|---|---|---|
| Main | Page background, typography, primary/secondary palette | src/themes/main/ |
| Header | App bar background, text colors | src/themes/header/ |
| Sidebar | Sidebar drawer background, nav item colors | src/themes/sidebar/ |
| Footer | Footer background and text | src/themes/footer/ |
Each region folder contains three mode files:
src/themes/
main/
default.ts ← light
dark.ts ← dark
semi-dark.ts ← semi-dark (dark sidebar + light content)
header/ sidebar/ footer/ (same structure)
Modifying colors
Open the token file for the region you want to change and edit the MUI createTheme object:
// src/themes/main/default.ts
import { createTheme } from '@mui/material/styles';
export const mainTheme = createTheme({
palette: {
mode: 'light',
primary: {
main: '#7352C7',
light: '#9E86DB',
dark: '#3F1D9B',
contrastText: '#fff',
},
secondary: {
main: '#E44D26',
contrastText: '#fff',
},
background: {
default: '#F5F7FA',
paper: '#FFFFFF',
},
},
typography: {
fontFamily: '"Noir Pro", "Roboto", "Helvetica", "Arial", sans-serif',
h1: { fontSize: '2rem', fontWeight: 700 },
},
shape: {
borderRadius: 8,
},
});The project ships the Noir Pro font family via @assets/fonts/noir-pro/styles.css. It is
imported in the root layout. If you switch to a Google Font, import it in
src/app/[lang]/layout.tsx and update the fontFamily in your theme token files.
Dark mode
Enable dark mode by swapping the main (and optionally the other region) themes to their dark
variants. This is what Demo 4 does in src/components/Demo4Layout/themes/index.ts:
// src/components/Demo4Layout/themes/index.ts
import { mainThemeDark } from '@/themes/main/dark';
import { headerThemeDark } from '@/themes/header/dark';
import { sidebarThemeDark } from '@/themes/sidebar/dark';
import { footerThemeDark } from '@/themes/footer/dark';
import { createJumboTheme } from '@jumbo/utilities/helpers';
export const CONFIG4 = {
THEME: createJumboTheme(mainThemeDark, headerThemeDark, sidebarThemeDark, footerThemeDark),
};To give your default layout a dark theme, update src/config/index.ts to import the dark files.
Runtime theme switching
The CustomizerSettings drawer lets users switch theme modes at runtime without a page reload.
It is controlled by AppProvider context (src/components/AppProvider/AppProvider.tsx). The
context stores the currently selected mode and region themes, and JumboTheme reads from it to
re-render with the updated theme.
If you want to implement your own dark mode toggle:
'use client';
import { useJumboTheme } from '@jumbo/components/JumboTheme';
import { mainTheme } from '@/themes/main/default';
import { mainThemeDark } from '@/themes/main/dark';
export function DarkModeToggle() {
const { muiTheme, setMuiTheme } = useJumboTheme();
const isDark = muiTheme.palette.mode === 'dark';
return (
<button onClick={() => setMuiTheme(isDark ? mainTheme : mainThemeDark)}>
{isDark ? 'Light mode' : 'Dark mode'}
</button>
);
}Styling components with sx
The MUI sx prop accepts any CSS property alongside theme tokens:
<Box
sx={{
bgcolor: 'primary.main',
color: 'primary.contrastText',
p: 3,
borderRadius: 2,
boxShadow: (theme) => theme.shadows[4],
}}
>
Content
</Box>Emotion's styled API
Use @emotion/styled for component-level styles that need full CSS access:
import styled from '@emotion/styled';
import { Box } from '@mui/material';
export const PageWrapper = styled(Box)(({ theme }) => ({
backgroundColor: theme.palette.background.default,
minHeight: '100vh',
padding: theme.spacing(3),
[theme.breakpoints.down('md')]: {
padding: theme.spacing(2),
},
}));AppRouterCacheProvider
The root layout wraps the entire tree in AppRouterCacheProvider from @mui/material-nextjs:
// src/app/[lang]/layout.tsx
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
export default async function RootLayout({ children }) {
return (
<html>
<body>
<AppRouterCacheProvider>
<JumboTheme init={CONFIG.THEME}>
{children}
</JumboTheme>
</AppRouterCacheProvider>
</body>
</html>
);
}This is required for MUI + Emotion to work correctly with Next.js Server Components. It ensures styles are collected during SSR and deduped on the client. See the Server Components guide for more details.