みかづきブログ・カスタム

基本的にはちょちょいのほいです。

Next.js + TypeScript + styled-components + SCSSの雛形をつくる 💻

最近は、静的なウェブサイトも、動的なウェブサイトも、デスクトップアプリもNext.jsで開発しています。
もっというと、スマホアプリもExpoを使っているのですが、ExpoはReactではあるものの、Next.jsではないので今回は除外します。

Next.jsのすべての機能をフル活用できているかというと、全然そんなことはないのですが、全てをNext.js + TypeScript + styled-components + SCSSで開発すると、プロジェクトを跨いでカスタムフックやコンポーネントの使い回しが可能なため、非常に効率的です。
また、Webpackなどで自作の開発環境を構築するよりも、他のエンジニアと共同作業しやすいので、とても助かってます。

そんなこんなで、Next.jsをセットアップする機会が多い今日この頃ですが、なんだか毎回同じコマンドを打っている気がするので、思い切って雛形を作ってみました。

それぞれブランチを分けてリポジトリにコミットしています。
今後も細かい調整を入れていくことになると思いますが、当面はこの雛形をベースに開発していこうと思う所存です。

Next.js + TypeScript + styled-components + SCSS

App Router

リポジトリ

github.com

設定した項目

styled-componentsの導入

yarn add styled-components
yarn add -D @types/styled-components

next.config.jsの設定

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  compiler: {
    styledComponents: {
      ssr: true
    }
  }
};

module.exports = nextConfig;

StyledComponentsRegistryの作成

// StyledComponentsRegistry.tsx

'use client'

import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
import { ReactNode, useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
 
export function StyledComponentsRegistry({
  children
}: {
  children: ReactNode
}) {
  const [ styledComponentsStyleSheet ] = useState(() => new ServerStyleSheet());

  useServerInsertedHTML(() => {
    const styles = styledComponentsStyleSheet.getStyleElement();

    styledComponentsStyleSheet.instance.clearTag();

    return <>{ styles }</>;
  })

  if (typeof window !== 'undefined') {
    return <>{ children }</>
  };

  return (
    <StyleSheetManager sheet={ styledComponentsStyleSheet.instance }>
      { children }
    </StyleSheetManager>
  );
}
// layout.tsx
import type { Metadata } from 'next';
import { Noto_Sans_JP } from 'next/font/google';
import { StyledComponentsRegistry } from '@/styling/StyledComponentsRegistry';
import { PageStateProvider } from '@/contexts/PageStateContext';

const noto = Noto_Sans_JP({
  subsets: [ 'latin' ]
});

export const metadata: Metadata = {
  title: 'App Router',
  description: 'description'
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="ja">
      <body className={ noto.className }>
        <PageStateProvider>
          <StyledComponentsRegistry>{ children }</StyledComponentsRegistry>
        </PageStateProvider>
      </body>
    </html>
  );
}

SCSSの導入

yarn add sass

Page Router

リポジトリ

github.com

設定した項目

styled-componentsの導入

yarn add styled-components
yarn add -D @types/styled-components babel-plugin-styled-components

.babelrcの作成

// .babelrc
{
  "presets": [
    "next/babel"
  ],
  "plugins": [[
    "styled-components", {
      "ssr": true
    }]
  ]
}

_document.tsxの編集

// _document.tsx

import Document, { DocumentContext } from 'next/document';
import { ServerStyleSheet } from 'styled-components';

export default class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () => {
        return originalRenderPage({
          enhanceApp: (App) =>
            function MyDocument(props) {
              return sheet.collectStyles(<App {...props} />);
            }
        });
      };

      const initialProps = await Document.getInitialProps(ctx);

      return {
        ...initialProps,
        styles: (
          <>
            { initialProps.styles }
            { sheet.getStyleElement() }
          </>
        )
      };
    } finally {
      sheet.seal();
    }
  }
}

SCSSの導入

yarn add sass

Electron

リポジトリ

github.com

設定した項目

styled-componentsの導入

yarn add styled-components
yarn add -D @types/styled-components babel-plugin-styled-components

.babelrcの作成

// .babelrc
{
  "presets": [
    "next/babel"
  ],
  "plugins": [[
    "styled-components", {
      "ssr": true
    }]
  ]
}

_document.tsxの編集

// _document.tsx

import Document, { DocumentContext } from 'next/document';
import { ServerStyleSheet } from 'styled-components';

export default class MyDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () => {
        return originalRenderPage({
          enhanceApp: (App) =>
            function MyDocument(props) {
              return sheet.collectStyles(<App {...props} />);
            }
        });
      };

      const initialProps = await Document.getInitialProps(ctx);

      return {
        ...initialProps,
        styles: (
          <>
            { initialProps.styles }
            { sheet.getStyleElement() }
          </>
        )
      };
    } finally {
      sheet.seal();
    }
  }
}

SCSSの導入

yarn add sass