Easily set up multi-theming in Sitecore JSS using Next.js and Tailwind CSS (Part 1)
Multi-theming refers to the ability to switch between different visual themes within an application. Themes can include changes to colors, typography, spacing, and other styling elements.
This article will provide step-by-step guidance on configuring multi-theming within a Sitecore JSS Headless/XM Cloud-based solution. The tutorial will specifically focus on utilizing NextJS and TailwindCSS to achieve this setup.
The process is structured into two distinct phases, each dedicated to the meticulous task of establishing a multi-theme configuration and effectively harnessing its capabilities within the NextJS application. In this article, we'll look into configuring the multi-theming.
Configure multi-theming
Let's explore the process of configuring multi-theming and understand the steps involved in customizing and managing multiple themes.
Theme mappings
To begin, include the following variable in your environment variables. When referring to theme mapping, it essentially signifies the correlation between a specific site and its associated theme.
NEXT_PUBLIC_THEME_MAPPINGS = {
"primary":["site1"],
"secondary":["site2", "site3"],
}
The NEXT_PUBLIC_THEME_MAPPINGS
variable contains the theme mapping configurations. It's structured as an object, where the key represent the theme name, and the corresponding values are arrays of associated sites. It's important to note that the site names in the array should match those defined in Sitecore.
NEXT_PUBLIC
as these variables are needed at client-side. Make sure to prefix theme mapping variable with NEXT_PUBLIC
as these variables are needed at client-side. If the same site is configured for multiple themes, it is noteworthy that the initial detected theme will be applied.Theme context
Create theme context
We're using React context to make our themes work smoothly. First, we need to set up the context and organize the theme details in ThemeContext. Check the image below to see what ThemeContext is all about.
//ThemeContext.tsx
import { createContext, useContext } from 'react';
export const ALL_THEMES = Object.keys(JSON.parse(process.env.NEXT_PUBLIC_THEME_MAPPINGS || '{}'));
type Themes = typeof ALL_THEMES;
export type ThemeName = Themes[number];
export type ThemeFile = {
[key in ThemeName]: unknown;
};
export const ThemeContext = createContext<ThemeName>(ALL_THEMES[0]);
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const useTheme = (themeFile?: ThemeFile) => {
const themeName = useContext(ThemeContext);
const themeData = themeFile ? themeFile[themeName] : undefined;
return { themeName, themeData };
};
We can get mainly two variables from the context.
themeName
- This tells us the name of the current theme.themeData
- This provides the details of the theme linked to the current theme.
Use theme context
Now that our theme context is set up, we just have to use it in the app. For this article, which concentrates on Sitecore's JSS NextJS solution, remember to wrap the entire app with the theme context. You can refer to it at src/pages/_app.tsx
//_app.tsx
import { useState } from 'react';
import config from 'temp/config';
import { ThemeContext } from 'lib/context/ThemeContext';
function App({ Component, pageProps }: AppProps<SitecorePageProps>): JSX.Element {
const { ...rest } = pageProps;
const sites = JSON.parse(config.sites);
const currentSite = sites.find(
(_) => _.name === pageProps.layoutData?.sitecore?.context?.site?.name
);
const [site] = useState(currentSite || sites[0]);
return (
<ThemeContext.Provider value={getTheme(site.name)}>
<Component {...rest} />
</ThemeContext.Provider>
);
}
export default App;
config
will be generated automatically in /temp
folder whenever the "bootstrap" command is invoked in the package.json file. The reference to config.sites
corresponds to the SITES variable, and it's essential to ensure that this variable is defined as an environment variable.getTheme
function for now, We'll discuss it in the next section.Detect current theme
Having successfully set up the theme context, the next step involves detecting the current theme and assigning its value to the theme context.
In this part, we bring in the getTheme
function. Its job is to figure out and give us the current theme based on the current site. Create a new file in src/lib/multisite/theme-mapping.ts
.
This function takes the current site name as an argument. Using the NEXT_PUBLIC_THEME_MAPPINGS environment variable, it figures out and gives us the current theme. Take a look at the code snippet below for a quick understanding.
//theme-mapping.ts
import { ThemeName } from 'lib/context/ThemeContext';
export const getTheme = (siteName: string): ThemeName => {
// This object should be configured manually for theming
const themesAssociatedToSites: Record<ThemeName, Array<string>> = JSON.parse(
process.env.NEXT_PUBLIC_THEME_MAPPINGS || '{}'
);
const _themes = Object.keys(themesAssociatedToSites) as unknown as ThemeName;
let currentTheme = Object.keys(themesAssociatedToSites)[0] as ThemeName;
for (let i = 0; i < _themes.length; i++) {
if (themesAssociatedToSites[_themes[i] as ThemeName].includes(siteName))
return (currentTheme = _themes[i] as ThemeName);
}
return currentTheme;
};
Refer /pages/_app.tsx
to understand how the above function is being used to detect the current theme.
Configure themes
It's time to set up Tailwind configurations for multiple themes. In this example, we'll configure our app with primary
and secondary
themes. To do this, install the tailwindcss-themer plugin for multi-theming.
There are a couple of basic steps to follow beforehand.
Setup base configuration file
Setup theme configuration files
Export themes
Configure tailwind
Now, create a folder named theme
in the src
directory. We will place theme configuration files in this folder, which includes the following files.
index.js
tailwind.base.js (base-configuration-file)
tailwind.primary.js (theme-configuration-file)
tailwind.secondary.js (theme-configuration-file)
Setup base configuration file
This configuration file contains base theme configurations that are common for all themes, such as spacing, breakpoints, etc.
//sample of tailwind.base.js
module.exports = {
extend: {
name: 'base',
screens: {
lg: '1200px',
ml: '960px',
md: '720px',
sm: '460px',
xs: '320px',
},
spacing: {
0: '0',
1: '4px',
2: '8px',
3: '16px',
4: '24px',
5: '32px',
6: '64px',
7: '128px',
8: '256px',
},
}
}
Setup theme configuration files
This configuration files contains theme specific configurations such as typography, colors, etc.
If you want to have a look at the files you can find theme in src/theme
folder.
//tailwind.primary.js
module.exports = {
name: 'primary',
extend: {
fontSize: {
// Desktop font sizes
xxl: ['7.5rem', '100%'], //120px 120px
xl: ['3.5rem', '100%'], //56px 56px
l: ['3rem', '125%'], //48px 60px
m: ['2.25rem', '125%'], //36px 45px
s: ['1.5rem', '124%'], //24px 30px
xs: ['1.125rem', '100%'], //18px 18px
xxs: ['0.875rem', '120%'], //14px 16.8px
body: ['0.875rem', '157%'], //14px 22px
button: ['1rem', '1.125rem'], //16px 18px
'text-link': ['1rem', '1.125rem'], //16px 18px
caption: ['1rem', '0.875rem'], //16px 14px
small: ['0.75rem', '130%'], //12px 15.6px
legal: ['0.75rem', '130%'], //12px 15.6px
base: ['1rem', '1.125rem'], //16px 18px
},
colors: {
'light-gray': '#F8F6F4',
'dark-gray': '#686869',
gray: '#C4BFB6',
primary: 'red',
darkprimary: 'orange',
secondary: '#000000',
white: '#FFFFFF',
black: '#000000',
transparent: 'transparent',
},
fontWeight: {
demi: '500',
heavy: '600',
bold: '700',
medium: '450',
regular: '400',
light: '300',
extralight: '200',
},
},
};
//tailwind.secondary.js
module.exports = {
name: 'secondary',
extend: {
fontSize: {
xxl: ['3.5rem', '114%'], //56px 63.84px
xl: ['3.5rem', '114%'], //56px 63.84px
l: ['2rem', '125%'], //32px 40px
m: ['1.5rem', '2rem'], //24px 32px
s: ['1.25rem', '1.75rem'], //20px 28px
xs: ['1rem', '1.25rem'], //16px 20px
xxs: ['0.75rem', '1rem'], //12px 16px
body: ['0.875rem', '157%'], //14px 21.98px
'large-body': ['1.125rem', '133%'], //18px 23.94px
button: ['0.875rem', '120%'], //14px 16.8px
'text-link': ['0.875rem', '0.875rem'], //14px 14px
small: ['0.75rem', '130%'], //12px 15.6px
legal: ['0.625rem', '130%'], //10px 13px
base: ['1rem', '1.125rem'], //16px 18px
},
colors: {
'light-gray': '#F9F9F9',
'dark-gray': '#54585A',
gray: '#D2D1D0',
primary: 'blue',
darkprimary: 'yellow',
secondary: '#000000',
white: '#FFFFFF',
black: '#000000',
transparent: 'transparent',
'light-black': '#454545',
},
fontWeight: {
bold: '700',
heavy: '600',
'semi-bold': '600',
medium: '500',
regular: '400',
'extra-light': '300',
},
},
};
Export themes
Now that configuration files are all set up, Export them to use it. Create a index.js
file that exports the themes. You can find this file in the same folder as other theme configuration files.
//index.js
const base = require('./tailwind.base');
const primary = require('./tailwind.primary');
const secondary = require('./tailwind.secondary');
module.exports = {
base: base,
themes: [primary, secondary],
};
Configure tailwind
In the previous step, we’ve exported the multiple themes that can be configured in Tailwind. It is time to configure the tailwind.config.js
to apply themes in Tailwind.
First of all, set the base configuration file to setup the base theme object in theme
key.
Now, Leverage the tailwindcss-themer plugin. This plugin expects an object with two keys.
defaultTheme
: Provide default theme as a fallback.themes
: It expects an array of themes that are configured in previous steps.
Please refer to the code snippet below to understand it.
//tailwind.config.js
const app = require('./src/theme');
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx}'],
plugins: [
require('tailwindcss-themer')({
defaultTheme: {
extend: app.themes[0], //Provide default theme as a fallback
},
themes: app.themes, //Provide array of themes which are configured in previous steps.
}),
],
theme: app.base, //Provide base theme
};
By following the above steps, multiple themes are successfully configured.
Now that we've successfully configured the multi-theming, Please refer How to leverage multi-theming as a next step to leverage multi-theming in the app.
Subscribe to my newsletter
Read articles from Saad Patel directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by