react-hot-toastのtoast.custom()を共通利用する
開発環境
- next 14.0.4
- react-hot-toast 2.4.1
- tailwindcss 3
前提
react-hot-toastはの基本的な使い方の説明は省きます。
また、プロジェクトにreact-hot-toastはインストール済みで、各コンポーネントからtoastAPIが利用できる状態を想定しています。
本題
react-hot-toastは簡単にトーストを導入できるライブラリです。
デフォルトでsuccess, errorをサポートしており、以下のように呼び出すことで簡単にトーストを利用できます。
toast.success('ログインしました')
toast.error('ログインに失敗しました');
また、それぞれのスタイルをカスタマイズする際は以下のようにToasterコンポーネントのtoastOptionsで定義できます。
<Toaster
toastOptions={{
style: {
minWidth: 360,
height: 40,
padding: 8,
borderRadius: 8,
},
success: {
style: {
border: '1px solid #...',
background: '#...',
color: '#...',
},
},
error: {
style: {
border: '1px solid #...',
background: '#...',
color: '#...',
},
icon: <CustomIcon />,
},
}}
position="top-center"
/>
customはいつ使う?
何かデータを操作したときのフィードバックとしてsuccess, errorで事足りることがほとんどだと思いますが、それら以外の通知(例、警告や補足など)を実装したいときにcustomを利用すると便利です。
ただ、toast.custom()はデフォルトのスタイルが当たっていません。
その分カスタマイズ性は高く用途によってはとても便利です。
customの困りどころ
前述したように、デフォルトのスタイルが当たっていない為、デフォルトのsuccess, errorと同じように利用する(例、色違いなど)となるとアニメーション含めスタイルを当てていくことになります。
また、JSXを渡す形になるので無駄なコードが増える可能性もあり、少し共通利用しにくいです。
そこで、カスタム用の関数を作成してみました。
alert以外も任意のものを追加できる形にしています。
export const customToast = {
alert: (message: string): ReactNode =>
toast.custom(({ visible }) => (
<div
className={`
flex h-10 min-w-[360px] items-center gap-2 rounded-lg
border border-yellow-400 bg-yellow-50 p-2
text-sm leading-5 text-yellow-400 shadow
${visible ? 'animate-scale-in-top' : 'animate-scale-out-top'}
`}
>
<CustomIcon />
<span>{message}</span>
</div>
)),
};
呼び出しはこんな感じです。
<button onClick={() => customToast.alert('こんにちは')}>
こんにちはボタン
</button>
おまけ
アニメーションの部分をTailwindで再現するためにanimate-scale-in-top、animate-scale-out-topの2つのクラスを追加しています。
import type { Config } from 'tailwindcss';
const config: Config = {
...(省略)
theme: {
extend: {
animation: {
'scale-in-top':
'scale-in-top 0.4s cubic-bezier(0.250, 0.460, 0.450, 0.940) both',
'scale-out-top':
'scale-out-top 0.1s cubic-bezier(0.550, 0.085, 0.680, 0.530) both',
},
keyframes: {
'scale-in-top': {
'0%': {
transform: 'scale(0)',
'transform-origin': '50% 0%',
opacity: '1',
},
to: {
transform: 'scale(1)',
'transform-origin': '50% 0%',
opacity: '1',
},
},
'scale-out-top': {
'0%': {
transform: 'scale(1)',
'transform-origin': '50% 0%',
opacity: '1',
},
to: {
transform: 'scale(0)',
'transform-origin': '50% 0%',
opacity: '1',
},
},
},
},
},
};
export default config;
下のサイトで簡単にアニメーションクラスを拡張できるのでぜひ活用してみてください。