One of the most significant features of PrimeReact is being design agnostic so that theming does not rely on a certain styling like Material, Bootstrap, Fluent or a specific CSS library. This is achieved via the flexible theming system, read more for detailed information at PrimeReact theming docs.
The theme of your choice can easily be bundled within your app however how about making it dynamic so that your users can choose it dynamically at runtime. Wouldn't it be awesome to switch your entire app from material to bootstrap then to a custom theme with one click? Let's see how it works.
Install PrimeReact
Begin with installing PrimeReact, refer to the get started section for details.
Extract the Themes
PrimeReact provides various themes including Material, Bootstrap, FluentUI and PrimeOne with dark mode alternatives. These are provided at primereact/resources/themes folder so first thing to do it creating an scss file that imports the one from node_modules. One file is for material and the other for bootstrap dark.
src/theme-md-light-indigo.scss
@import "primereact/resources/themes/md-light-indigo/theme.css";
src/theme-bootstrap-4-dark-purple.scss
@import "primereact/resources/themes/bootstrap4-dark-purple/theme.css";
Eject from CRA
Custom webpack configuration is necessary so if you are using create-react-app in your app eject first.
npm run eject
Webpack
Eject creates a config folder with the webpack configuration files in it. Open the webpack.config.js file and add a constant to identify PrimeReact themes.
...
const sassPrimeReactThemeRegex = /theme-.*.(scss|sass)$/;
...
Next step is adding a rule to process the themes.
...
{
test: sassPrimeReactThemeRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
},
'sass-loader',
true
),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
// Opt-in support for SASS (using .scss or .sass extensions).
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: [sassModuleRegex, sassPrimeReactThemeRegex],
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
},
'sass-loader'
),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
...
Then override the getStyleLoaders to pass a parameter that processes a theme in lazy manner. View lazy style tag to learn how it works.
const getStyleLoaders = (cssOptions, preProcessor, isPrimeReactTheme) => {
const loaders = [
{
loader: require.resolve('style-loader'),
options: {
injectType: isPrimeReactTheme ? "lazyStyleTag" : "styleTag"
}
},
...
View the file at github for the content.
Change Theme
The config part is now over, let's switch to app side to change the themes on the fly.
...
import { useState, useLayoutEffect } from 'react';
import { InputText } from 'primereact/inputtext';
import { InputTextarea } from 'primereact/inputtextarea';
import sagaBlue from './theme-saga-blue.scss'
import './App.css';
function App() {
const [selectedThemeModule, setSelectedThemeModule] = useState(sagaBlue);
useLayoutEffect(() => {
selectedThemeModule.use();
return () => { selectedThemeModule.unuse() };
}, []); // eslint-disable-line react-hooks/exhaustive-deps
const changeTheme = (theme) => {
import(`./${theme}.scss`).then((module) => {
if (selectedThemeModule) {
selectedThemeModule.unuse();
}
module.use();
setSelectedThemeModule(module);
});
}
return (
<div className="card">
<div className="flex align-items-center justify-content-center mb-4">
<button onClick={() => changeTheme('theme-saga-blue')} className="mr-3 cursor-pointer p-link">
<img src="assets/images/themes/saga-blue.png" width="50" alt="saga-blue" />
</button>
<button onClick={() => changeTheme('theme-vela-blue')} className="mr-3 cursor-pointer p-link">
<img src="assets/images/themes/vela-blue.png" width="50" alt="vela-blue" />
</button>
<button onClick={() => changeTheme('theme-arya-blue')} className="mr-3 cursor-pointer p-link">
<img src="assets/images/themes/arya-blue.png" width="50" alt="arya-blue" />
</button>
<button onClick={() => changeTheme('theme-md-light-indigo')} className="mr-3 cursor-pointer p-link">
<img src="assets/images/themes/md-light-indigo.svg" width="50" alt="md-light-indigo" />
</button>
<button onClick={() => changeTheme('theme-md-dark-indigo')} className="mr-3 cursor-pointer p-link">
<img src="assets/images/themes/md-dark-indigo.svg" width="50" alt="md-dark-indigo" />
</button>
<button onClick={() => changeTheme('theme-bootstrap4-light-purple')} className="mr-3 cursor-pointer p-link">
<img src="assets/images/themes/bootstrap4-light-purple.svg" width="50" alt="bootstrap4-light-purple" />
</button>
<button onClick={() => changeTheme('theme-bootstrap4-dark-purple')} className="mr-3 cursor-pointer p-link">
<img src="assets/images/themes/bootstrap4-dark-purple.svg" width="50" alt="bootstrap4-dark-purple" />
</button>
</div>
<div className="formgrid grid p-fluid">
<div className="field col-12 md:col-6">
<label htmlFor="firstname">Firstname</label>
<InputText id="firstname" />
</div>
<div className="field col-12 md:col-6">
<label htmlFor="lastname">Lastname</label>
<InputText id="lastname" />
</div>
<div className="field col-12">
<label htmlFor="address">Address</label>
<InputTextarea id="address" rows="4" />
</div>
<div className="field col-12 md:col-6">
<label htmlFor="city">City</label>
<InputText id="city" className="inputfield w-full" />
</div>
</div>
</div>
);
}
export default App;
Live Demo
That's it, view the live demo to take it for a test run. The app theme changes between Material, Bootstrap and custom PrimeOne Themes. You can even add your own themes to the mix as well so that your users can choose their own experience.
Alternative Implementation
There is also another option available that uses a link tag, refer to the github repo for the sample implementation that does not use lazy style tag and utilizes a link tag instead.
Wrap up
PrimeReact has a flexible theming system that allows you to implement design agnostic applications. Without this approach, switching from one style to another will require rewriting your entire app with another library. For example, assume you're using Material-UI and then decided to change to ReactStrap or ReactBootstrap. It will be a major rewrite whereas with PrimeReact it will happen with one touch just by changing the theme.