- Adding themes to Next.js with styled-components, mobx, and typescript
- Getting Started
- 1. Install
- 2. Create a file for the theme constants
- 3. Add styled-components to the next.config.js file
- 4. Add ServerStyleSheet to _document.tsx
- 5. Create utility functions to allow persisting data with localStorage
- 6. Create a UI Store with mobx
- 7. Create a global style with styled-components
- 8. Creating the StyleProvider
- 9. Add typing to the theme
- 10. Wrap the provider around pages/_app.tsx
- Test that it works in pages/index.tsx
- How to create a Theme in React/Typescript (Context API) with styled-components
- Create a new project
- styled-components
- Create theme variables and ThemeProps interface:
- Create a global styles with createGlobalStyle from styled-components and set the theme variables:
- Create a Theme context:
- Create a hook function to switch the theme:
- Create a TogglerButton component to use hook function and switch the theme when clicked:
- And create the styles to TogglerButton:
- And finally, we need to add the Context, ThemeProvider, GlobalStyle and ThemeToggler components to App.tsx:
- How to Configure Theming or Theme Switching with React and TypeScript
- Setup React with TypeScript
- What is styled-component & what it is being used?
- Folder structure and multiple theme file creations
Adding themes to Next.js with styled-components, mobx, and typescript
Let your users decide. Provide the option to switch seamlessly between themes with styled-components and mobx .
Getting Started
- Installing dependencies
- Creating the theme constants
- Configuring styled-components with Next.js
- Adding styled-components’ server-side functionality
- Persisting data
- Creating a mobx store
- Create a global style
- Creating the StyleProvider
- Adding types to the theme
- Making sure it works
1. Install
- The dependencies: yarn add styled-components mobx mobx-react
- The dev-dependencies: yarn add -D @types/styled-components
2. Create a file for the theme constants
themes.constants.ts
In this file, we’re going to define the themes and other relavant constants.
export const DEFAULT_THEME = 'dark'
3. Add styled-components to the next.config.js file
In the nextConfig object, add the key-pair values:
4. Add ServerStyleSheet to _document.tsx
Add the getInitialProps method to the MyDocument class shown below.
If you haven’t already created _document.tsx , add it to your pages folder. And paste the following:
import Document, < Html, Head, Main, NextScript, DocumentContext, >from 'next/document'; import < ServerStyleSheet >from 'styled-components'; class MyDocument extends Document < static async getInitialProps(ctx: DocumentContext) < const sheet = new ServerStyleSheet(); const originalRenderPage = ctx.renderPage; try < ctx.renderPage = () =>originalRenderPage( < enhanceApp: (App) =>(props) => sheet.collectStyles( />), >); const initialProps = await Document.getInitialProps(ctx); return < . initialProps, styles: [ <> >, ], >; > finally < sheet.seal(); >> render() < return ( ); > > export default MyDocument;
5. Create utility functions to allow persisting data with localStorage
export const setStorage = (key: string, value: unknown) => < window.localStorage.setItem(key, JSON.stringify(value)); >; export const getStorage = (key: string) => < const value = window.localStorage.getItem(key); if (value) < return JSON.parse(value); >>;
6. Create a UI Store with mobx
import < action, makeAutoObservable, observable >from 'mobx'; import < setStorage >from './utils'; type Themes = 'dark' | 'light'; class UiStore < @observable private _theme: Themes = 'light'; @observable private _initializing: boolean = true; constructor() < makeAutoObservable(this); >get theme() < return this._theme; >get initializing() < return this._initializing; >@action toggleTheme() < this._theme = this._theme === 'light' ? 'dark' : 'light'; setStorage('theme', this._theme); >@action changeTheme(nameOfTheme: Themes) < this._theme = nameOfTheme; >@action finishInitializing() < this._initializing = false; >> export const uiStore = new UiStore();
7. Create a global style with styled-components
We’ll soon be able to access the theme in the styled-components in the following manner.
global-styles.ts
export const GlobalStyle = createGlobalStyle` * < margin: 0; padding: 0; box-sizing: border-box; >html < background: $<(< theme >) => theme.colors.body>; > `;
8. Creating the StyleProvider
import < observer >from 'mobx-react'; import < useEffect, ReactNode >from 'react'; import < ThemeProvider >from 'styled-components'; import < uiStore >from './uiStore'; import < DEFAULT_THEME, THEMES >from './theme.constants'; import < GlobalStyle >from './global-styles` import < getStorage, setStorage >from './utils'; interface OnlyChildren < children: ReactNode >const StyleProviderComponent = (props: OnlyChildren) => < const < children >= props; const < theme >= uiStore; useEffect(() => < if (!getStorage('theme')) < setStorage('theme', DEFAULT_THEME); >const storageThemeName = getStorage('theme'); uiStore.changeTheme(storageThemeName); uiStore.finishInitializing(); >, []); return ( > ); >; export const StyleProvider = observer(StyleProviderComponent);
9. Add typing to the theme
import 'styled-components'; declare module 'styled-components' < export interface DefaultTheme < name: string; colors: < primary: string; textPrimary: string; >; > >
10. Wrap the provider around pages/_app.tsx
import type < AppProps >from 'next/app'; import < StyleProvider >from './style-provider'; function MyApp(< Component, pageProps >: AppProps) < return ( /> ); > export default MyApp;
Test that it works in pages/index.tsx
import type < NextPage >from 'next'; import Head from 'next/head'; import < uiStore >from './uiStore'; const Home: NextPage = () => < const < initializing >= uiStore; if (!initializing) < return Loading.
; > return ( <> >) > export default Home
You can access the theme in styled-components by adding a callback function (ex: $<(< theme >) => theme.colors.textPrimary> ) in between the back ticks.
const Button = styled.button` background: $<(< theme >) => theme.colors.body>; border: 1px solid $<(< theme >) => theme.colors.accent>; color: $<(< theme >) => theme.colors.textPrimary>; `
How to create a Theme in React/Typescript (Context API) with styled-components
Hello everyone, in this super fast tutorial, I’ll teach you how to create a theme in a React/Typescript application with styled-components, let’s go?
Create a new project
yarn create react-app *your-application-name* --template=typescript
styled-components
yarn add styled-components
yarn add @types/styled-components -d
Create theme variables and ThemeProps interface:
export interface ThemeProps background: string; text: string; > export const darkTheme: ThemeProps = background: 'var(--dark-background)', text: 'var(--dark-text)', >; export const lightTheme: ThemeProps = background: 'var(--light-background)', text: 'var(--light-text)', >;
Create a global styles with createGlobalStyle from styled-components and set the theme variables:
import createGlobalStyle, withTheme > from 'styled-components'; import ThemeProps > from './themes'; type GlobalThemeProps = theme: ThemeProps; >; const globalStyle = createGlobalStyle` :root < //dark-mode --dark-background: #1A1B27; --dark-text: #F5F5F7; //light-mode --light-background: #f2f2f2; --light-text: #2E0509; >* < margin: 0; padding: 0; box-sizing: border-box; outline: 0; >body < -webkit-font-smoothing: antialiased; height: 100vh; width: 50vw; margin: 0 auto; background-color: $ <(theme >: GlobalThemeProps) => theme.background>; display: flex; justify-content: center; align-items: center; > h1 < font-size: 3.375rem; color: $ <(theme >: GlobalThemeProps) => theme.text>; > `; export default withTheme(globalStyle);
In the h1 and body styles we can already see an example of applying the themes, but we still need to create the context and hook function.
Create a Theme context:
import React from 'react'; import ThemeProvider > from 'styled-components'; import useThemeMode > from '../../hooks/useThemeMode'; import lightTheme, darkTheme > from '../../styles/themes'; const ThemeContext: React.FC = ( children >) => const theme > = useThemeMode(); const themeMode = theme === 'dark' ? darkTheme : lightTheme; return ThemeProvider theme=themeMode>>children>/ThemeProvider>; >; export default ThemeContext;
Context they are ways to save the value of states outside the component’s scope.
Create a hook function to switch the theme:
import useEffect, useState > from 'react'; export const useThemeMode = () => const [theme, setTheme] = useState('dark'); const setMode = (mode: string) => window.localStorage.setItem('theme', mode); setTheme(mode); >; const themeToggler = () => (theme === 'dark' ? setMode('light') : setMode('dark')); useEffect(() => const localTheme = window.localStorage.getItem('theme'); localTheme && setTheme(localTheme); >, []); return theme, themeToggler >; >; export default useThemeMode;
Here we are creating a theme state, fetching its initial value from the browser’s storage and changing its value when the setMode function is called.
Create a TogglerButton component to use hook function and switch the theme when clicked:
import HiMoon > from 'react-icons/hi'; import FaSun > from 'react-icons/fa'; import * as S from './styles'; interface ThemeTogglerProps themeToggler: () => void; > function TogglerButton( themeToggler >: ThemeTogglerProps) return ( S.Container> label htmlFor="checkbox" className="switch"> input id="checkbox" type="checkbox" onClick=themeToggler> onChange= => false> checked=window.localStorage.getItem('theme') === 'light'> /> S.Icons className="slider round"> window.localStorage.getItem('theme') !== 'light' ? ( <> HiMoon style= <marginLeft: '6.3px', height: '10px' >> /> /> ) : ( <> FaSun size=0> style= <marginLeft: '41px', height: '10px' >> /> /> )> /S.Icons> /label> /S.Container> ); > export default TogglerButton;
- When creating this component we use an outside library for the icons, so we need to install that too, it’s called React Icons:
And create the styles to TogglerButton:
import styled from 'styled-components'; export const Container = styled.div` .switch position: relative; display: inline-block; width: 4rem; height: 1.5rem; > .switch input opacity: 0; width: 0; height: 0; > .slider position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: $( theme >) => theme.background>; -webkit-transition: 0.2s; transition: 0.2s; box-shadow: 0 0 2px $( theme >) => theme.text>; > .slider:before position: absolute; content: ''; height: 14px; width: 14px; left: 7px; bottom: 5px; background-color: $( theme >) => theme.background>; -webkit-transition: 0.2s; transition: 0.2s; > input:checked + .slider background-color: $( theme >) => theme.background>; > input:checked + .slider:before -webkit-transform: translateX(35px); -ms-transform: translateX(35px); transform: translateX(35px); > /* Rounded sliders */ .slider.round border-radius: 34px; > .slider.round:before border-radius: 50%; > `; export const Icons = styled.span` width: 100%; display: flex; justify-content: space-between; top: 25%; align-items: center; svg color: $( theme >) => theme.text>; z-index: 11; > `;
Here in this style we can see the theme usage in some properties.
Like in this code snippet below:
.slider position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: $( theme >) => theme.background>; -webkit-transition: 0.2s; transition: 0.2s; box-shadow: 0 0 2px $( theme >) => theme.text>; >
We are changing the background-color according to the theme’s background variable.
And finally, we need to add the Context, ThemeProvider, GlobalStyle and ThemeToggler components to App.tsx:
import ThemeProvider > from 'styled-components'; import TogglerButton from './components/TogglerButton'; import GlobalStyle from './styles/global'; import ThemeContext from './contexts/ThemeContext'; import lightTheme, darkTheme > from './styles/themes'; import useThemeMode from './hooks/useThemeMode'; function App() const theme, themeToggler > = useThemeMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme; return ( ThemeContext> ThemeProvider theme=themeMode>> GlobalStyle /> header> TogglerButton themeToggler=themeToggler> /> /header> h1>theme>/h1> /ThemeProvider> /ThemeContext> ); > export default App;
Run yarn and then yarn start in your terminal and it’s done!
if you want to add more colors, you need to set it in the global.ts file and then reference it to a variable in the themes.ts file.
The goal with this article was to make a more direct tutorial, but any questions just send there in the comments that, I’ll be answering. If you need more references, I have some examples of usage in repositories on my Github.
That’s it for today, guys, I hope you enjoyed the article and that it can help you and your team in some way.
How to Configure Theming or Theme Switching with React and TypeScript
I had a project requirement to support theming in react-based applications. So there I have come up with a simple and quick solution to build an application with a pre-defined theme (mainly background color, text color, fonts, etc). Let’s check how I have approached solving the problem.
Themeing & Theme Swtiching
* leverage styled-componet (npm library)
* define a theme model (holds the property I want to change — such as background color, text color)
* create multiple theme & apply switch function
* leveraged React Context API to store theme value
Above I have three different colors and based on theme value, I can change the background color and text color.
Setup React with TypeScript
Let’s set up an application with React, TypeScript and styled-component. I will be using create-react-app command to build the application.
npx create-react-app redux-demo-app --template typescript
npm install --save @types/react-router-dom
npm install --save bootstrap react-bootstrap
npm install --save node-sass
npm install --save styled-components @types/styled-components
Also, import @import «~bootstrap/scss/bootstrap»; as well as your scss file. All project is now setup and it will run the application on your web browser.
What is styled-component & what it is being used?
styled-components: A flexible way to style React components with CSS. It provides out-of-the-box theming support using a wrapper component called, . This component is responsible for providing the theme to all other React components that are wrapped within it.
Folder structure and multiple theme file creations
Before implementing a theme file, let’s create some folder structure for which I can keep respective files (.tsx, .ts, and .scss) files to configure my codebase.
Create styled.d.ts file for TypeScript and here we will be defining our theming properties.
declare module 'styled-components'
export interface DefaultTheme