Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 15, 2022 10:33 pm GMT

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

  1. Installing dependencies
  2. Creating the theme constants
  3. Configuring styled-components with Next.js
  4. Adding styled-components' server-side functionality
  5. Persisting data
  6. Creating a mobx store
  7. Create a global style
  8. Creating the StyleProvider
  9. Adding types to the theme
  10. 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

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To