Chakra UIを薄く軽量に使う
追記
はじめに
この記事で説明しているChakraProviderの使い方は公式ドキュメントで推奨されているものではありませんのでご了承ください。
概要
-
@chakra-ui/react
ではなく@chakra-ui/provider
のChakraProvider
を使う -
theme
はextendTheme
を使わずcomponents
のthemeを外す -
@chakra-ui/cli
で必要な分だけの型生成を行う
実装例
改めましてこんにちは
こんにちは。Chakra UIと言うReactのUIライブラリをご存知でしょうか。
僕はSassを使ったFLOCSSから入り、 styled-componentsを通り、Chakra UIへとたどり着きました。
HTMLの構造と一緒にスタイルを管理できるのと、TypeScriptの型が活かせるので書き心地がとても良いです。
Chakra UIでは以下のようにsytled-systemに沿った形式でスタイルを当てることが出来ます。
const BuildWidthChakraSection: FC<HTMLChakraProps<section>> = (props) => (
<chakra.section {...props}>
<chakra.h2 fontSize="20" fontWeight="bold">
Built with Chakra UI ⚡️
</chakra.h2>
<chakra.p mt="8" fontSize="16" color="#999">
A collection of websites and projects built with Chakra UI
</chakra.p>
</chakra.section>
);
またChakra UIでは<Modal />
など機能を持ったコンポーネントを提供してくれます。
導入もすごい簡単で依存packageを追加して<ChakraProvider />
でアプリケーションを囲ってあげればとりあえず動かすことが出来ます。
Chakra UI bundleサイズ大きい問題
HTMLにそのままCSSを当てるような形式でとても直感的に書けて簡単にUIを作れるChakra UIですが、一つ大きな問題があります。
bundleサイズめちゃでかいんです。
<Modal />
や<Toast />
,<Drawer />
など機能が作り込まれたコンポーネントをしっかり使っている場合は納得がいくのですが、styled-systemライクのスタイルの当て方をしたい僕のようなCSS手書き頑張るマンにとっては悩みの種となっています。
- Chakra UI無し next build
// _app.tsx
import { AppProps } from "next/app";
function MyApp({ Component, pageProps }: AppProps) {
return (
<Component {...pageProps} />
);
}
export default MyApp;
Page Size First Load JS
┌ ○ / 242 B 77.5 kB
├ /_app 0 B 77.2 kB
└ ○ /404 194 B 77.4 kB
+ First Load JS shared by all 77.2 kB
├ chunks/framework-84154cdd319403d1.js 45.2 kB
├ chunks/main-aff9f5cd95c9bd16.js 30.8 kB
├ chunks/pages/_app-633f4db406d1f71e.js 490 B
└ chunks/webpack-fd82975a6094609f.js 727 B
- Chakra UIあり next build
// _app.tsx
import { AppProps } from "next/app";
import { ChakraProvider } from "@chakra-ui/react";
function MyApp({ Component, pageProps }: AppProps) {
return (
<ChakraProvider>
<Component {...pageProps} />
</ChakraProvider>
);
}
export default MyApp;
Page Size First Load JS
┌ ○ / 242 B 164 kB
├ /_app 0 B 163 kB
└ ○ /404 194 B 164 kB
+ First Load JS shared by all 163 kB
├ chunks/framework-7dc8a65f4a0cda33.js 45.2 kB
├ chunks/main-aff9f5cd95c9bd16.js 30.8 kB
├ chunks/pages/_app-5884c6fb68a34fcb.js 86.4 kB
└ chunks/webpack-9b0e45c24ba97727.js 1.07 kB
_app.jsのbundleサイズが86kBも増えています。
86kB。。。すごく大きいです。
bundleサイズの問題はissueとして上がってますが、根本解決はなかなか難しそうですね。。
なぜbundleサイズが大きいのか
@chakra-ui/react
が提供する<ChakraProvider />
に<ToastProvider />
が含まれている。
冒頭で紹介したChakra UIのセットアップで、アプリケーションを<ChakraProvider />
で囲えば動かすことが出来ると紹介しましたが、この<ChakraProvider />
には<Toast />
をすぐに使えるよう<ToastProvider />
が組み込まれています。
@chakra-ui/providerを使う
<Toast />
を使うのであればありがたいのですが、CSS手書き頑張るマンにとっては不要です。
幸いこのProviderは@chakra-ui/provider
が提供する<ChakraProvider />
を使っていたのでそちらを直接使うことにしました。
import { AppProps } from "next/app";
- import { ChakraProvider } from "@chakra-ui/react";
+ import { theme} from "@chakra-ui/react";
+ import { ChakraProvider } from "@chakra-ui/provider";
function MyApp({ Component, pageProps }: AppProps) {
return (
- <ChakraProvider>
+ <ChakraProvider theme={theme}>
<Component {...pageProps} />
</ChakraProvider>
);
}
@chakra-ui/provider
の<ChakraProvider />
はデフォルトのthemeが適用されていないので、@chakra-ui/react
からthemeを読み込むようにしています。
差し替えた後のbundleサイズを見てみると
Page Size First Load JS
┌ ○ / 242 B 125 kB
├ /_app 0 B 125 kB
└ ○ /404 194 B 125 kB
+ First Load JS shared by all 125 kB
├ chunks/framework-84154cdd319403d1.js 45.2 kB
├ chunks/main-aff9f5cd95c9bd16.js 30.8 kB
├ chunks/pages/_app-0b8d1aa93e24065f.js 47.8 kB
└ chunks/webpack-9b0e45c24ba97727.js 1.07 kB
_app.jsのbundleサイズが86kBから47kBと40kB近く下がりました🙌
<Toast />
使わないだけでこれだけ下げることが出来るのは嬉しいですね。
themeも削ります
実はthemeもbundleサイズを肥大化させる要因になっています。
デフォルトのthemeは以下のようになっており、設定がオブジェクトして書かれているとてもシンプルなものです。
このthemeにはChakra UIが提供するコンポーネントのthemeも含まれているためなかなかのボリュームがあります。
しかしながら、このデフォルトのthemeに書かれているimport components from "./components"
はCSS手書き頑張るマンに取っては不要です。
components以外のthemeも自身で定義したいので丸ごと外します。
import { AppProps } from "next/app";
- import { theme} from "@chakra-ui/react";
import { ChakraProvider } from "@chakra-ui/provider";
function MyApp({ Component, pageProps }: AppProps) {
return (
- <ChakraProvider theme={theme}>
+ <ChakraProvider>
<Component {...pageProps} />
</ChakraProvider>
);
}
bundleサイズがどう変わったか見てみます。
Page Size First Load JS
┌ ○ / 242 B 107 kB
├ /_app 0 B 107 kB
└ ○ /404 194 B 107 kB
+ First Load JS shared by all 107 kB
├ chunks/framework-84154cdd319403d1.js 45.2 kB
├ chunks/main-aff9f5cd95c9bd16.js 30.8 kB
├ chunks/pages/_app-d0c3f30cc5460bc8.js 29.6 kB
└ chunks/webpack-9b0e45c24ba97727.js 1.07 kB
_app.jsのbundleサイズが47kBから30kBと17kB近く下がりました🙌
Chakra UI追加前のbundleサイズと比較するとChakra UI自体で約30kBが追加されることとなりました。
これでも単体のstyled-componentsなどに比べて重たいのは間違い無いのですが、開発体験の良さがあるのでこれ以上ワガママは言いません。
(これ以上お手軽にbundleサイズ下げる方法を思いつきませんでした。)
オリジナルのthemeの型定義を生成する
先ほど重たいという理由でデフォルトのthemeを外しましたが、外しただけだと一つ困ったことが起こります。
themeとして存在しないはずのkeyが型定義に残っているためスタイルを当てる時の候補に出てきます。
存在しないkeyを使用してしまうとスタイルが反映されないので@chakra-ui/cli
を使用してカスタムthemeの型定義を作ってデフォルトの型定義を上書きしましょう。
packageを追加
$ npm install -D @chakra-ui/cli
themeのファイルを指定して実行
npx chakra-cli tokens <path/to/your/theme.(js|ts)>
とすると、node_modules内のChakra UIの型定義がカスタムthemeで上書きされます。
どのような型定義ができているかを見たい場合は--out theming.types.ts
とのようにオプションを足して実行すると型定義ファイルを好きな場所に生成できます。
あとはnpm install
時に、この型生成が自動的に行われるようpostinstall
でscriptを指定しましょう。
"scripts": {
"gen:theme-typings": "chakra-cli tokens <path/to/your/theme.(js|ts)>",
"postinstall": "npm run gen:theme-typings"
},
extendThemeは使わない
公式ドキュメントに載っているextendTheme
を使ってしまうと、既存のthemeが入り込んでしまうため、themeを1から作ります。
デフォルトのthemeの値を消しただけです笑
// theme.ts
import { ChakraTheme } from "@chakra-ui/react";
/**
* typography関連を管理したい場合はここに記述する
* @see https://github.com/chakra-ui/chakra-ui/blob/main/packages/theme/src/foundations/typography.ts
*/
const typography: Pick<
ChakraTheme,
"fonts" | "fontSizes" | "fontWeights" | "letterSpacings" | "lineHeights"
> = {
fonts: {},
fontSizes: {},
fontWeights: {},
letterSpacings: {},
lineHeights: {},
};
/**
* marginやcolorを管理したい場合はここに記述する
* @see https://github.com/chakra-ui/chakra-ui/tree/main/packages/theme/foundations
*/
const foundations: Pick<
ChakraTheme,
| "borders"
| "breakpoints"
| "colors"
| "radii"
| "shadows"
| "sizes"
| "space"
| "transition"
| "zIndices"
> &
typeof typography = {
borders: {},
breakpoints: {},
colors: {},
radii: {},
shadows: {},
sizes: {},
space: {},
transition: {
property: {},
easing: {},
duration: {},
},
zIndices: {},
...typography,
};
/**
* global stylesを変更したい場合はここに記述する
* themeのkeyを使うことができます
*
* @see https://chakra-ui.com/docs/styled-system/global-styles
*/
const styles: ChakraTheme["styles"] = {
global: {
body: {},
"*, *::before, &::after": {},
},
};
/**
* themeの設定はここに記述する
* @see https://chakra-ui.com/docs/styled-system/color-mode#updating-the-theme-config
*/
const config: ChakraTheme["config"] = {
useSystemColorMode: false,
initialColorMode: "light",
cssVarPrefix: "chakra",
};
export const theme: ChakraTheme = {
...foundations,
direction: "ltr",
components: {},
styles,
config,
};
あとは必要に応じてthemeで使いたいkey, valueを増やしていけば問題ないです。
space: {
0: "0",
4: "4px",
8: "8px",
12: "12px",
16: "16px",
24: "24px",
32: "32px",
48: "48px",
64: "64px",
},
出来上がったもの
当初の半分以下の約30kBのbundleサイズでChakra UIを使うことが出来るようになりました。
Page Size First Load JS
┌ ○ / 11.1 kB 120 kB
├ /_app 0 B 109 kB
└ ○ /404 194 B 109 kB
+ First Load JS shared by all 109 kB
├ chunks/framework-7dc8a65f4a0cda33.js 45.2 kB
├ chunks/main-aff9f5cd95c9bd16.js 30.8 kB
├ chunks/pages/_app-f8685f270fc3a879.js 31.6 kB
└ chunks/webpack-9b0e45c24ba97727.js 1.07 kB
まとめ
冒頭と同じですが
-
@chakra-ui/react
ではなく@chakra-ui/provider
のChakraProvider
を使う -
theme
はextendTheme
を使わずcomponents
のthemeを外す -
@chakra-ui/cli
で必要な分だけの型生成を行う
実装例
Discussion