<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Zach Nugent</title>
    <description>The latest articles on Forem by Zach Nugent (@mrzachnugent).</description>
    <link>https://forem.com/mrzachnugent</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F848143%2F2b6256b6-b347-46e0-a604-b670afca3ad6.png</url>
      <title>Forem: Zach Nugent</title>
      <link>https://forem.com/mrzachnugent</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mrzachnugent"/>
    <language>en</language>
    <item>
      <title>Adding themes to Next.js with styled-components, mobx, and typescript</title>
      <dc:creator>Zach Nugent</dc:creator>
      <pubDate>Fri, 15 Apr 2022 22:33:45 +0000</pubDate>
      <link>https://forem.com/mrzachnugent/adding-themes-to-nextjs-with-styled-components-mobx-and-typescript-2cd3</link>
      <guid>https://forem.com/mrzachnugent/adding-themes-to-nextjs-with-styled-components-mobx-and-typescript-2cd3</guid>
      <description>&lt;h2&gt;
  
  
  Dark Mode vs Light Mode?
&lt;/h2&gt;

&lt;p&gt;Let your users decide. Provide the option to switch seamlessly between themes with &lt;code&gt;styled-components&lt;/code&gt; and &lt;code&gt;mobx&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Installing dependencies&lt;/li&gt;
&lt;li&gt;Creating the theme constants&lt;/li&gt;
&lt;li&gt;Configuring styled-components with Next.js&lt;/li&gt;
&lt;li&gt;Adding styled-components' server-side functionality&lt;/li&gt;
&lt;li&gt;Persisting data&lt;/li&gt;
&lt;li&gt;Creating a mobx store&lt;/li&gt;
&lt;li&gt;Create a global style&lt;/li&gt;
&lt;li&gt;Creating the StyleProvider&lt;/li&gt;
&lt;li&gt;Adding types to the theme &lt;/li&gt;
&lt;li&gt;Making sure it works&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Install
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The dependencies: &lt;code&gt;yarn add styled-components mobx mobx-react&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The dev-dependencies: &lt;code&gt;yarn add -D @types/styled-components&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Create a file for the theme constants
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;themes.constants.ts&lt;/code&gt;&lt;br&gt;
In this file, we're going to define the themes and other relavant constants.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Colours
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const COLOURS = {
  black: '#000000',
  white: '#FFFFFF'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Dark theme
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const DARK_THEME = {
  name: 'dark',
  background: COLOURS.black,
  textPrimary: COLOURS.white
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Light theme
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const LIGHT_THEME = {
  name: 'light',
  background: COLOURS.white,
  textPrimary: COLOURS.black
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Export the Default theme
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const DEFAULT_THEME = 'dark'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Export the themes
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const THEMES = {
  dark: DARK_THEME,
  light: LIGHT_THEME,
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  3. Add styled-components to the &lt;code&gt;next.config.js&lt;/code&gt; file
&lt;/h2&gt;

&lt;p&gt;In the nextConfig object, add the key-pair values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  compiler: {
    styledComponents: true,
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Add &lt;code&gt;ServerStyleSheet&lt;/code&gt; to &lt;code&gt;_document.tsx&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Add the &lt;code&gt;getInitialProps&lt;/code&gt; method to the &lt;code&gt;MyDocument&lt;/code&gt; class shown below.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you haven't already created &lt;code&gt;_document.tsx&lt;/code&gt;, add it to your &lt;code&gt;pages&lt;/code&gt; folder. And paste the following:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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 = () =&amp;gt;
        originalRenderPage({
          enhanceApp: (App) =&amp;gt; (props) =&amp;gt;
            sheet.collectStyles(&amp;lt;App {...props} /&amp;gt;),
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: [
          &amp;lt;&amp;gt;
            {initialProps.styles}
            {sheet.getStyleElement()}
          &amp;lt;/&amp;gt;,
        ],
      };
    } finally {
      sheet.seal();
    }
  }
  render() {
    return (
      &amp;lt;Html&amp;gt;
        &amp;lt;Head&amp;gt;
        &amp;lt;/Head&amp;gt;
        &amp;lt;body&amp;gt;
          &amp;lt;Main /&amp;gt;
          &amp;lt;NextScript /&amp;gt;
        &amp;lt;/body&amp;gt;
      &amp;lt;/Html&amp;gt;
    );
  }
}

export default MyDocument;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Create utility functions to allow persisting data with localStorage
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;utils.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const setStorage = (key: string, value: unknown) =&amp;gt; {
  window.localStorage.setItem(key, JSON.stringify(value));
};
export const getStorage = (key: string) =&amp;gt; {
  const value = window.localStorage.getItem(key);

  if (value) {
    return JSON.parse(value);
  }
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Create a UI Store with &lt;code&gt;mobx&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;uiStore.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Create a global style with &lt;code&gt;styled-components&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We'll soon be able to access the theme in the styled-components in the following manner.&lt;br&gt;
&lt;code&gt;global-styles.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const GlobalStyle = createGlobalStyle`
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }

html {
  background: ${({ theme }) =&amp;gt; theme.colors.body};
}

`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. Creating the StyleProvider
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;style-provider.tsx&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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) =&amp;gt; {
  const { children } = props;
  const { theme } = uiStore;

  useEffect(() =&amp;gt; {
    if (!getStorage('theme')) {
      setStorage('theme', DEFAULT_THEME);
    }
    const storageThemeName = getStorage('theme');
    uiStore.changeTheme(storageThemeName);
    uiStore.finishInitializing();
  }, []);

  return (
    &amp;lt;ThemeProvider theme={THEMES[theme]}&amp;gt;
      &amp;lt;GlobalStyle /&amp;gt;
      {children}
    &amp;lt;/ThemeProvider&amp;gt;
  );
};

export const StyleProvider = observer(StyleProviderComponent);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9. Add typing to the theme
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;default-theme.d.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'styled-components';

declare module 'styled-components' {
  export interface DefaultTheme {
    name: string;
    colors: {
      primary: string;
      textPrimary: string;
    };
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  10. Wrap the provider around &lt;code&gt;pages/_app.tsx&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { AppProps } from 'next/app';
import { StyleProvider } from './style-provider';

function MyApp({ Component, pageProps }: AppProps) {
  return (
    &amp;lt;StyleProvider&amp;gt;
      &amp;lt;Component {...pageProps} /&amp;gt;
    &amp;lt;/StyleProvider&amp;gt;
  );
}

export default MyApp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test that it works in &lt;code&gt;pages/index.tsx&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { NextPage } from 'next';
import Head from 'next/head';
import { uiStore } from './uiStore';

const Home: NextPage = () =&amp;gt; {
  const { initializing } = uiStore;

  if (!initializing) {
    return &amp;lt;h1&amp;gt;Loading...&amp;lt;/h1&amp;gt;;
  }

  return (
    &amp;lt;&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;
          Adding themes to Next.js with styled-components, mobx, and typescript
        &amp;lt;/title&amp;gt;
        &amp;lt;meta name='description' content='Tutorial by Zach Nugent' /&amp;gt;
        &amp;lt;link rel='icon' href='/favicon.ico' /&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; uiStore.toggleTheme()}&amp;gt;Switch theme&amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  )
}
export default Home

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can access the theme in styled-components by adding a callback function (ex: &lt;code&gt;${({ theme }) =&amp;gt; theme.colors.textPrimary}&lt;/code&gt;) in between the back ticks.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const Button = styled.button`
  background: ${({ theme }) =&amp;gt; theme.colors.body};
  border: 1px solid ${({ theme }) =&amp;gt; theme.colors.accent};
  color: ${({ theme }) =&amp;gt; theme.colors.textPrimary};
`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks for reading 🙂&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>tutorial</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
