0.4.5 • Published 2 years ago

@andrewizbatista/use-mui-themes v0.4.5

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

useTheme

An improved Material UI useTheme hook that supports multiple themes with assets/images coupled with the theme mode (dark/light).

Created by♥ @andrewizbatista

Table of Contents

Getting Started

Prerequisites

This package has peerDependencies that are required for you to include in your app's dependencies:

{
  "dependencies": {
    "@mui/material": "^5.10.2",
    "next": "^12.2.5",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  }
}

Installing

yarn

yarn add @andrewizbatista/use-mui-themes

npm

npm install @andrewizbatista/use-mui-themes

Usage

1. Define your Theme related types

It's required that you define these types so TypeScript can do its magic.

// src/themes/types.ts

/**
 * Names of the themes you support in your app.
 */
export type ThemeNames = 'foo' | 'bar';

/**
 * Keys for all the assets you support in your app.
 */
export type AssetKeys = 'companyLogo' | 'heroImage';

2. Create your Theme(s)

Create all the themes you want your app to support. These themes are Material UI v5 Themes with an additional assets prop which allows you to have different images between themes and even modes (Light/Dark).

// src/themes/foo/index.ts

import { createTheme } from '@andrewizbatista/use-mui-themes';
/**
 * Tip: Divide your theme into multiple files
 * because Material themes can get big.
 */
import { paletteLight, paletteDark } from './palette';
import { assetsLight, assetsDark } from './assets';
import { typography } from './typography';
// Types
import { ThemeNames, AssetKeys } from './types';


/**
 * Foo Theme
 */
export const fooTheme = createTheme<ThemeNames, AssetKeys>({
  name: 'foo',
  dark: {
    palette: paletteDark,
    assets: assetsDark,
    typography
  },
  light: {
    palette: paletteLight,
    assets: assetsLight,
    typography
  },,
});

3. Create your ThemeContext

Feed all the themes you defined into your context and set some defaults.

// src/themes/index.ts

import { createThemeContext } from '@andrewizbatista/use-mui-themes';
// Themes
import { fooTheme } from './foo';
import { barTheme } from './bar';
// Types
import { ThemeNames, AssetKeys } from './types';

/**
 * ThemeContext
 */
export const { ThemeContext, ThemeProvider, useTheme } = createThemeContext<ThemeNames, AssetKeys>({
  defaultTheme: 'foo',
  defaultThemeMode: 'dark',
  themes: {
    foo: fooTheme,
    bar: barTheme,
  },
});

4. Support Next.js SSR (Server Side Rendering)

4.1. Changes in the Document (_document.tsx)

For your Next.js app to support SSR you have to add an Emotion Cache to the NextDocument.getInitialProps.

I've included a function that does exactly that. See getInitialPropsWithEmotionServer for the full breakdown.

// pages/_document.tsx

import NextDocument, { DocumentContext, Html, Main, Head, NextScript } from 'next/document';
import { getInitialPropsWithEmotionServer } from '@andrewizbatista/use-mui-themes';

export default class Document extends NextDocument {

  static async getInitialProps(ctx: DocumentContext) {
    return getInitialPropsWithEmotionServer(ctx, 'css'); // 'css' is the Emotion Cache Key

  }

  public render() {
    return (
      <Html lang="en">
        <Head>
          {...}
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

4.2. Changes in the App (_app.tsx)

We provide a type AppWithEmotionProps that is extensible and already supports the emotionCache prop. Also don't forget to wrap your app with the ThemeProvider

// pages/_app.tsx

import { AppWithEmotionProps } from '@andrewizbatista/use-mui-themes';
import { ThemeProvider } from 'src/themes';

export default function App({
  Component,
  pageProps,
  emotionCache,
}: AppWithEmotionProps<PageProps>) {
  const { myCustomProp } = pageProps;

  return (
    <ThemeProvider emotionCache={emotionCache}>
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

/**
 * Additional props you might have in `pageProps`
 */
interface PageProps {
  myCustomProp: string;
}

5. Use the useTheme hook

With your app wrapped with the ThemeProvider you can now use the useTheme hook everywhere you want.

function MyComponent() {
  const {
    state: { palette, assets },
    actions: { changeTheme, toggleThemeMode },
  } = useTheme();

  return (
    <Box>
      <Image src={assets.companyLogo} />
      <Button color={palette.primary.main} onClick={() => changeTheme('bar')}>
        Change Theme to "Bar"
      </Button>
      <Button color={palette.secondary.main} onClick={toggleThemeMode}>
        Toggle Light/Dark Mode
      </Button>
    </Box>
  );
}

Contributing

Want to help? Feel free to open an Issue or create a Pull Request and let's get started 🚀

License

MIT © André Batista (@andrewizbatista) 🎉