An Interest In:
Web News this Week
- February 29, 2024
- February 28, 2024
- February 27, 2024
- February 26, 2024
- February 25, 2024
- February 24, 2024
- February 23, 2024
Adding themes to Next.js with styled-components, mobx, and typescript
Dark Mode vs Light Mode?
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.
- Colours
const COLOURS = { black: '#000000', white: '#FFFFFF'}
- Dark theme
const DARK_THEME = { name: 'dark', background: COLOURS.black, textPrimary: COLOURS.white}
- Light theme
const LIGHT_THEME = { name: 'light', background: COLOURS.white, textPrimary: COLOURS.black}
- Export the Default theme
export const DEFAULT_THEME = 'dark'
- Export the themes
export const THEMES = { dark: DARK_THEME, light: LIGHT_THEME,}
3. Add styled-components to the next.config.js
file
In the nextConfig object, add the key-pair values:
compiler: { styledComponents: true, },
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(<App {...props} />), }); const initialProps = await Document.getInitialProps(ctx); return { ...initialProps, styles: [ <> {initialProps.styles} {sheet.getStyleElement()} </>, ], }; } finally { sheet.seal(); } } render() { return ( <Html> <Head> </Head> <body> <Main /> <NextScript /> </body> </Html> ); }}export default MyDocument;
5. Create utility functions to allow persisting data with localStorage
utils.ts
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
uiStore.ts
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
style-provider.tsx
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 ( <ThemeProvider theme={THEMES[theme]}> <GlobalStyle /> {children} </ThemeProvider> );};export const StyleProvider = observer(StyleProviderComponent);
9. Add typing to the theme
default-theme.d.ts
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 ( <StyleProvider> <Component {...pageProps} /> </StyleProvider> );}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 <h1>Loading...</h1>; } return ( <> <Head> <title> Adding themes to Next.js with styled-components, mobx, and typescript </title> <meta name='description' content='Tutorial by Zach Nugent' /> <link rel='icon' href='/favicon.ico' /> </Head> <button onClick={() => uiStore.toggleTheme()}>Switch theme</button> </> )}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.
Example:
const Button = styled.button` background: ${({ theme }) => theme.colors.body}; border: 1px solid ${({ theme }) => theme.colors.accent}; color: ${({ theme }) => theme.colors.textPrimary};`
Thanks for reading
Original Link: https://dev.to/mrzachnugent/adding-themes-to-nextjs-with-styled-components-mobx-and-typescript-2cd3
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To