I'm using bootstrap for the interface of the website I'm developing. I am also planning to integrate the bootswatch themes to my site. I have created a dropdown menu containing the different themes of bootswatch. My problem is how can I switch themes when I click one of the themes in the dropdown menu?
7 Answers
Fiddle:http://jsfiddle.net/82AsF/ (click the themes dropdown in the navbar)
Give your links a class theme-link
and a data-theme
value of the theme name like so:
<a href="#" data-theme="amelia" class="theme-link">Amelia</a>
Then, define your themes in a global variable like so:
var themes = {
"default": "//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css",
"amelia" : "//bootswatch.com/amelia/bootstrap.min.css",
"cerulean" : "//bootswatch.com/cerulean/bootstrap.min.css",
"cosmo" : "//bootswatch.com/cosmo/bootstrap.min.css",
"cyborg" : "//bootswatch.com/cyborg/bootstrap.min.css",
"flatly" : "//bootswatch.com/flatly/bootstrap.min.css",
"journal" : "//bootswatch.com/journal/bootstrap.min.css",
"readable" : "//bootswatch.com/readable/bootstrap.min.css",
"simplex" : "//bootswatch.com/simplex/bootstrap.min.css",
"slate" : "//bootswatch.com/slate/bootstrap.min.css",
"spacelab" : "//bootswatch.com/spacelab/bootstrap.min.css",
"united" : "//bootswatch.com/united/bootstrap.min.css"
}
(please host these bootswatch css files on your own server when you do this - I'm only hotlinking because I don't have a server on hand)
Then, use the following jQuery code:
$(function(){
var themesheet = $('<link href="'+themes['default']+'" rel="stylesheet" />');
themesheet.appendTo('head');
$('.theme-link').click(function(){
var themeurl = themes[$(this).attr('data-theme')];
themesheet.attr('href',themeurl);
});
});

- 5,800
- 7
- 38
- 55
-
1Hi thanks for this. My question is would it be possible that even if the user tried to refresh the page, the applied theme is still there? Seems what happened to be is when I refresh it will go to default theme that I set.? – jackhammer013 Sep 28 '15 at 07:41
-
1@JoeneFloresca you can store that preference data in a multitude of ways, the simplest of which would be using a [cookie](https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie). You can also store their preferences in a database if your users have accounts. – Kevin Pei Sep 28 '15 at 14:31
the main problem with above solution if page is refreshed the theme is gone. i found this article very helpful regarding persistent theme in the browser because it save setting in browser's storage.
i hope it helps you

- 4,041
- 2
- 23
- 21
I based mine off of @KevinPei's answer to make an automatic dropdown using jQuery, Bootstrap, and Bootswatch to add a dropdown to the navbar that automatically updates the theme to the selected value.
Fiddle: https://jsfiddle.net/davfive/uwseLLzf/show
The code works great in practice. However, in jsfiddle, there are two odd behaviors:
- Sometimes the dropdown extends to the whole screen
- The dropdown's mouseover/hover color isn't showing in jsfiddle.
<html lang="en">
<head>
<!-- To switch themes, go to https://www.bootstrapcdn.com/bootswatch/?theme=0 -->
<link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/cerulean/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="navbar navbar-default navbar-static-top">
<div class="container-fluid">
<a class="navbar-brand">Bootswatch Theme Changer</a>
<div class="nav navbar-nav pull-right">
<li id="theme-button" class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Change Theme <b class="caret"></b></a>
<ul class="dropdown-menu ">
<li><a href="#" name="current">Cerulean</a></li>
<li class="divider"></li>
<li><a href="#" name="cerulean">Cerulean</a></li>
<li><a href="#" name="cosmo">Cosmo</a></li>
<li><a href="#" name="cyborg">Cyborg</a></li>
<li><a href="#" name="darkly">Darkly</a></li>
<li><a href="#" name="flatly">Flatly</a></li>
<li><a href="#" name="journal">Journal</a></li>
<li><a href="#" name="lumen">Lumen</a></li>
<li><a href="#" name="paper">Paper</a></li>
<li><a href="#" name="readable">Readable</a></li>
<li><a href="#" name="sandstone">Sandstone</a></li>
<li><a href="#" name="simplex">Simplex</a></li>
<li><a href="#" name="slate">Slate</a></li>
<li><a href="#" name="solar">Solar</a></li>
<li><a href="#" name="spacelab">Spacelab</a></li>
<li><a href="#" name="superhero">Superhero</a></li>
<li><a href="#" name="united">United</a></li>
<li><a href="#" name="yeti">Yeti</a></li>
</ul>
</li>
</div>
</div>
</div>
<script>
jQuery(function($) {
$('#theme-button ul a').click(function() {
if ($(this).attr('name') != 'current') {
var urlbeg = 'https://maxcdn.bootstrapcdn.com/bootswatch/3.3.7/'
var urlend = '/bootstrap.min.css'
var themeurl = urlbeg + $(this).text().toLowerCase() + urlend;
var link = $('link[rel="stylesheet"][href$="/bootstrap.min.css"]').attr('href', themeurl);
$('#theme-button ul a[name="current"]').text($(this).text());
}
});
});
</script>
</body>
</html>

- 323
- 4
- 13
-
Thanks for catching that and sorry that you had to. I forgot to include the script links in the for bootstrap and jquery. I've updated it and tested that it works outside of jsfiddle. – davfive Dec 13 '17 at 00:10
The solution provided by Kevin is great. I have just spent some time trying to figure out how I can make it work with local bootswatch libraries.
The typical href attribute value:
"~/lib/bootstrap/dist/css/bootstrap.min.css"
But in this case:
var themes = {
"default": "lib/bootstrap/dist/css/bootstrap.min.css",
"amelia": "lib/bootswatch/amelia/bootstrap.min.css",
"cerulean": "lib/bootswatch/cerulean/bootstrap.min.css",
"cosmo": "lib/bootswatch/cosmo/bootstrap.min.css",
"cyborg": "lib/bootswatch/cyborg/bootstrap.min.css",
"flatly": "lib/bootswatch/flatly/bootstrap.min.css",
"journal": "lib/bootswatch/journal/bootstrap.min.css",
"readable": "lib/bootswatch/readable/bootstrap.min.css",
"simplex": "lib/bootswatch/simplex/bootstrap.min.css",
"slate": "lib/bootswatch/slate/bootstrap.min.css",
"spacelab": "lib/bootswatch/spacelab/bootstrap.min.css",
"united": "lib/bootswatch/united/bootstrap.min.css"
}

- 4,445
- 6
- 28
- 48
you can try something like this
html:
<link href="/Themes/DefaultClean/Content/css/styles.css" id="theme-sheet" rel="stylesheet" type="text/css" />
javascript:
function setTheme(themeName) {
$('#theme-sheet').attr({ href: "https://bootswatch.com/slate/bootstrap.min.css" });;
}
where in the setTheme function will be called from your desired source such as dropdown or button.

- 343
- 2
- 14
Thanks at @davfive and his copy/paste one file sample.
Here are his sample upgraded to bootswatch 4.5 and jQuery 3.5 :
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- To switch themes, go to https://www.bootstrapcdn.com/bootswatch/?theme=0 -->
<link href="https://maxcdn.bootstrapcdn.com/bootswatch/4.5.0/flatly/bootstrap.min.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<a class="navbar-brand" href="index.html"><img src="images/logos/Logo.png" alt="logo">MyWebSite Name </a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarColor01" aria-controls="navbarColor01" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarColor01">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="index.html">home<span class="sr-only">(current)</span></a>
</li>
<li id="theme-button" class="dropdown">
<a href="#" class="dropdown-toggle nav-link" data-toggle="dropdown">Change Theme <b class="caret"></b></a>
<ul class="dropdown-menu ">
<li><a href="#" name="current">Flatly</a></li>
<li class="divider"></li>
<li><a href="#" name="flatly">Flatly</a></li>
<li><a href="#" name="superhero">Superhero</a></li>
<li><a href="#" name="united">United</a></li>
</ul>
</li>
</ul>
</div>
</nav>
<script>
jQuery(function($) {
$('#theme-button ul a').click(function() {
if ($(this).attr('name') != 'current') {
var urlbeg = 'https://maxcdn.bootstrapcdn.com/bootswatch/4.5.0/'
var urlend = '/bootstrap.min.css'
var themeurl = urlbeg + $(this).text().toLowerCase() + urlend;
var link = $('link[rel="stylesheet"][href$="/bootstrap.min.css"]').attr('href', themeurl);
$('#theme-button ul a[name="current"]').text($(this).text());
}
});
});
</script>
</body>
</html>
I also add home menu link and remove a lot of themes. Of course, you can add/change some.

- 1,988
- 1
- 17
- 31
Dynamic bootstrap lazy theme loader (+ automatical dark mode switcher).
This approcach allows you to change bootstrap themes without page reloading, just by changing your app's state.
TL;DR: Live Demo, source code.
At first, you need to configure your webpack
:
module: {
rules: [
// ...your loaders...
{
// your CSS loaders.
test: /\.css$/i,
use: ["style-loader", "css-loader"],
resourceQuery: {not: [/raw/]}, // exclude raw queries!
},
{
// Add a kind of raw loader:
resourceQuery: /raw/,
type: "asset/source",
}
]
},
optimization: {
splitChunks: {},
},
And add asset_source_loader.d.ts
into the project root to tell TypeScript type checker that you have ability to load modules as raw strings:
declare module "*?raw" {
const content: string;
export default content;
}
Then we can start to write our components.
index.tsx
(don't forget to install dependency: npm i react-helmet-async
):
import {HelmetProvider} from "react-helmet-async";
// ...
root.render(
<HelmetProvider>
<DarkModeProvider child={App} childProps={{someProp: 3.14}}/>
</HelmetProvider>
);
Main App
component:
interface AppProps extends DarkModeAware {
someProp: number,
}
class App extends React.Component<AppProps> {
render(): ReactNode {
return (
<ThemeLoader theme={this.props.isDarkMode ? "cyborg" : "cosmo"}
fallback={<div>Theme loading...</div>}>
<div>...Your app...</div>
</ThemeLoader>
);
}
}
InstalledThemes.tsx
(for simplicity, few themes are shown here, see a full list in demo project):
export type ThemeName = "default_bootstrap" | "cerulean" | "cosmo" | "cyborg"
type LazyStyleLoader = {
[key in ThemeName]: () => Promise<typeof import("*?raw")>
}
export const LazyThemeLoader: LazyStyleLoader = {
default_bootstrap: () => import("bootstrap/dist/css/bootstrap.min.css?raw"),
cerulean: () => import("bootswatch/dist/cerulean/bootstrap.min.css?raw"),
cosmo: () => import("bootswatch/dist/cosmo/bootstrap.min.css?raw"),
cyborg: () => import("bootswatch/dist/cyborg/bootstrap.min.css?raw"),
};
DarkModeProvider.tsx:
import React, {ReactNode} from "react";
export interface DarkModeAware {
isDarkMode: boolean;
}
// @see https://stackoverflow.com/a/69850582
// @see https://github.com/microsoft/TypeScript/issues/23182#issuecomment-379091887
type UndefinedIfEmptyObject<Obj extends Record<PropertyKey, unknown>> =
[keyof Obj] extends [never] ? undefined : Obj
export class DarkModeProvider<T extends DarkModeAware> extends React.Component<{
child: React.ComponentType<T>,
childProps: UndefinedIfEmptyObject<Omit<T, keyof DarkModeAware>>,
}, DarkModeAware> {
private darkModeQuery: MediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
state: DarkModeAware = {
isDarkMode: this.darkModeQuery.matches,
};
private darkModeListener = (event: MediaQueryListEvent) => {
this.setState({isDarkMode: event.matches});
};
componentDidMount() {
this.darkModeQuery.addEventListener("change", this.darkModeListener);
}
componentWillUnmount() {
this.darkModeQuery.removeEventListener("change", this.darkModeListener);
}
render(): ReactNode {
const darkModeAwaitedProps = Object.assign({}, this.props.childProps ?? {}, this.state) as T;
return React.createElement(this.props.child, darkModeAwaitedProps);
}
}
ThemeLoader.tsx
:
import React, {ReactNode} from "react";
import {Helmet} from "react-helmet-async";
import {LazyThemeLoader, ThemeName} from "./InstalledThemes";
interface Props {
theme: ThemeName,
children: ReactNode,
fallback: ReactNode,
}
interface State {
currentStyle?: string,
}
/**
* Bootstrap doesn't have builtin functionality for changing themes.
* So, there is a way to change the theme - reload the whole boostrap.min.css.
*/
export class ThemeLoader extends React.Component<Props, State> {
state: State = {
currentStyle: undefined,
};
private loadTheme(): void {
LazyThemeLoader[this.props.theme]().then((result) => {
this.setState({currentStyle: result.default});
}).catch((error) => {
this.setState(() => {
throw error;
});
});
}
componentDidMount() {
this.loadTheme();
}
componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) {
if (prevProps.theme !== this.props.theme) {
this.loadTheme();
}
}
render(): ReactNode {
if (this.state.currentStyle === undefined) {
return this.props.fallback;
}
return (
<React.Fragment>
<Helmet>
<style>{this.state.currentStyle}</style>
</Helmet>
{this.props.children}
</React.Fragment>
);
}
}

- 2,229
- 1
- 15
- 26