How can one have a theme switcher in primereact
Asked Answered
B

4

6

I would like to be in a position to switch between themes in primereact rather than import one theme and then it affects my whole app and I don't have an option to switch between dark or light mode.

Belgae answered 10/7, 2021 at 11:39 Comment(0)
W
2

I couldn't make it work with their provided function https://primereact.org/theming/#switchthemes

I have use useEffect for my case which changes theme dark <-> light based on prop.

First I copied the themes I wanted to the react vite public folder root. I named them light.css and dark.css. Then in useEffect I manipulated DOM:

  useEffect(() => {
    const link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = `${props.mode}.css`;
    document.head.appendChild(link);
    return () => {
      document.head.removeChild(link);
    };
  }, [props.mode]);

Now it works when props.mode change from light to dark and vice versa.

Winsor answered 26/1 at 15:3 Comment(1)
Apparently in the new version of Primereact you have to manipulate the dom otherwise everytime you change the theme it appends to the dom. My currenct solution was this;Belgae
C
7

Observing how they do it on the website www.primefaces.org/primereact/showcase/, open Developer view: Elements, and one can notice that choosing a different theme changes css file link in HTML header:

<link id="theme-link" rel="stylesheet" href="./themes/bootstrap4-light-blue/theme.css">

becomes

<link id="theme-link" rel="stylesheet" href="./themes/bootstrap4-light-purple/theme.css">

It is fairly easy to switch link element HREF from one to another.

This page talks about primereact theme switching: Switch Your React App Between Material, Bootstrap and Custom Themes at Runtime

But the method it describes is too convoluted, involves ejecting and custom webpack, to bundle all theme CSS files and import them programmatically, like that:

const changeTheme = (theme) => {
  import(`./${theme}.scss`).then((module) => {
    if (selectedThemeModule) {
      selectedThemeModule.unuse();
    }
    module.use();
    setSelectedThemeModule(module);
  });
}

Instead, grab the example repo where they do method of link HREF swap: github.com/mertsincan/primereact-dynamic-theming/

example-1 has code for the convoluted method from the above page, you can skip it and go to example-2, which is much simpler.

In a nutshell, add to 'public/index.html', in <header> section:

<link id="app-theme" rel="stylesheet" type="text/css" href="saga-blue.css">

And use this function:

const changeTheme = (theme) => {
  let themeLink = document.getElementById('app-theme');
  if (themeLink) {
    themeLink.href = theme + '.css';
  }
}

Then just call changeTheme(XXX) when theme XXX is clicked.

Next put .css files into the right place - just copy all node_modules/primereact/themes/*/theme.css files into public folder (giving them corresponding theme names). Some theme.css reference fonts - search for "url" in each file, and if present, copy corresponding fonts/ directory too.

I should mention that benefits of example-1 is using minified and bundled CSS files, so themes will be switching faster. If that's important, then follow the above linked tutorial and example-1. Also note that example-2 has very similar setup to example-1 (eject and custom webpack config), but only to copy css files to the right output folder, which can be skipped in favor of copying files by hand once.

Cleveite answered 1/9, 2021 at 8:38 Comment(1)
Thank you so much I will check the solution and get back to you as soon as I canBelgae
C
4

There is an even easier way to do this They do explain it a bit in the docs but it isn't as copy-paste in the docs

Here is what I did in my React app:

This is in my index.html file Define an element with an id to reference later in your code, and them also href to the directory where the styles are.

   <link id="app-theme" rel="stylesheet" href="/themes/lara-dark-blue/theme.css">

The styles should be copied from the node_modules of prime react Copy the dark and light theme from Lara

cp -r ./node_modules/primereact/resources/themes/lara-light-blue ./public/themes/lara-light-blue
cp -r ./node_modules/primereact/resources/themes/lara-dark-blue ./public/themes/lara-dark-blue

You can also just drag and drop them, but terminal is also nice ;)

Now in your React component, import PrimeReact from the api directory and then use the changeTheme utility function to change from dark to light and vice versa

import PrimeReact from 'primereact/api';

function App() {

  const [theme, setTheme] = useState('dark');

  const changeMyTheme = () => {
    const newTheme = theme === 'dark' ? 'light' : 'dark';
    PrimeReact?.changeTheme?.(`lara-${theme}-blue`, `lara-${newTheme}-blue`, 'app-theme', () =>
      setTheme(newTheme)
    );
  };

  return (
    <div>
      <header>
        <button
          className={`p-2 rounded ${theme === 'dark' ? 'bg-gray-100 text-black' : 'bg-gray-700 text-white'}`}
          onClick={() => changeMyTheme()}
        >
          <span className={`pr-1 pi pi-${theme === 'dark' ? 'sun' : 'moon'}`}></span>
         Change Theme
        </button>
      </header>
    </div>
  );
}

Here are the docs if you need to reference some more details as well as other themes available https://primereact.org/theming/

Contaminate answered 16/5, 2023 at 7:16 Comment(1)
I still couldn't make it work. In the end I just used useeffect and dom manipulation to switch theme. I advise others to do the same. ` useEffect(() => { const link = document.createElement("link"); link.rel = "stylesheet"; link.href = ${props.mode}.css; document.head.appendChild(link); return () => { document.head.removeChild(link); }; }, [props.mode]);`Winsor
W
2

I couldn't make it work with their provided function https://primereact.org/theming/#switchthemes

I have use useEffect for my case which changes theme dark <-> light based on prop.

First I copied the themes I wanted to the react vite public folder root. I named them light.css and dark.css. Then in useEffect I manipulated DOM:

  useEffect(() => {
    const link = document.createElement("link");
    link.rel = "stylesheet";
    link.href = `${props.mode}.css`;
    document.head.appendChild(link);
    return () => {
      document.head.removeChild(link);
    };
  }, [props.mode]);

Now it works when props.mode change from light to dark and vice versa.

Winsor answered 26/1 at 15:3 Comment(1)
Apparently in the new version of Primereact you have to manipulate the dom otherwise everytime you change the theme it appends to the dom. My currenct solution was this;Belgae
L
0

i am a late to the party but here's my solution for Next.js (Pages Router)

useLocalStorage of primereact is a bit weird so i had to use useLocalStorage from usehooks-ts

// _app.tsx
...
import { useLocalStorage } from "usehooks-ts";

export const prefersTheme = () => {
    if (typeof window === "undefined") {
        return 'light'
    }
    const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
    return prefersDarkScheme.matches ? 'dark' : 'light';
}

export default function App({ Component, pageProps }: AppProps) {
    const [theme, setTheme] = useLocalStorage("theme", prefersTheme)

    const toggleTheme = () => {
        setTheme((prevTheme) => (prevTheme === 'dark' ? 'light' : 'dark'));
    };

    useEffect(() => {
        // If a theme is set, save it to localStorage and apply the theme
        if (theme) {
            const existingLink = document.getElementById('theme-link');
            if (existingLink) {
                existingLink.remove();
            }

            const link = document.createElement('link');
            link.id = 'theme-link';
            link.rel = 'stylesheet';
            link.href = theme === 'dark'
                ? '/themes/soho-dark/theme.css'
                : '/themes/soho-light/theme.css';

            document.head.appendChild(link);
        }
    }, [theme]);

    ...
}

Make sure your themes are in public directory.

Lustrum answered 10/11 at 11:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.