この記事はShadcn/uiの SidebarコンポーネントをNext.jsで動作確認をしています。
SidebarコンポーネントをXで公表
Sidebar - shadcn/ui のドキュメント
Sidebar
sidebar.tsx
Sidebar.tsx-25コンポーネントを紹介します。
Next.js、Remix、Vite、Laravelで動作します。
サイドバー - shadcn/ui
この記事のコードについて
リポジトリのファイル配置
(sidebar-publice)フォルダ
Shadcn/uiの Sidebarコンポーネント 公式のサンプル
(sidebar)
自分が試した、Shadcn/UIのサイドバーコンポーネント
リポジトリ内のファイル
src
├── app
│ ├── (sidebar-publice) 公式のサンプル
│ │ ├── sample01
│ │ │ └── page.tsx
...
│ │ └── sample15
│ │ └── page.tsx
│ ├── (sidebar) 自分が試したサンプル
│ │ ├── 01
│ │ │ ├── layout.tsx
│ │ │ └── page.tsx
...
│ │ └── 23
│ │ ├── layout.tsx
│ │ └── page.tsx
│ ├── dashboard
│ ├── layout.tsx
│ └── page.tsx
├── components
│ ├── ui
│ │ └── **.tsx (Shadcn/UIのコンポーネント)
│ ├── app-sidebar_01.tsx
...
│ ├── app-sidebar_24.tsx
│ ├── app-sidebar_sample01.tsx
...
│ ├── app-sidebar_sample14.tsx
│ ├── calendars.tsx
│ ├── date-picker.tsx
│ ├── nav-actions.tsx
│ ├── nav-favorites.tsx
│ ├── nav-main.tsx
│ ├── nav-projects.tsx
│ ├── nav-secondary.tsx
│ ├── nav-user.tsx
│ ├── nav-workspaces.tsx
│ ├── search-form.tsx
│ ├── settings-dialog.tsx
│ ├── sidebar-left.tsx
│ ├── sidebar-opt-in-form.tsx
│ ├── sidebar-right.tsx
│ ├── team-switcher.tsx
│ └── version-switcher.tsx
├── hooks
│ └── use-mobile.tsx
└── lib
└── utils.ts
公式サンプルのルール 15種類
公式のサンプルはガッツリ作り込まれています。
Shadcn/UIのサイドバーのサンプルは全部で15種類、コンポーネントは25種類あります。
このBlog記事ではその15種類の公式サンプルのフォルダ名を
sidebar-01
sidebar-**と数字をつけて見ていきます。
app-sidebar_sample01.tsx
他に複数のファイルがある時があります。
自分が作ったサンプルのルール
公式サンプルはわかりにくかったので、わかりやすくしてみました。
リポジトリで、自分が作ったサンプルのフォルダ名は
01
と
01のサイドバーのファイル名
components/app-sidebar_01.tsx
02のサイドバーのファイル名
components/app-sidebar_02_01.tsx
(以下略)
と数字のみのフォルダ名にファイルを入れています。
※この記事に書いてあるコードは公式ドキュメントのコードになります。
※公式ドキュメントで重複しているサイドバーのファイル名には番号を振って識別できるようにしています。
※クッキーに保存されているデータは、他のサイドバーのサンプルに影響を与えています。
GitHub
masakinihirota/ShadcnUI_sidebar
25種のコンポーネントを使ったサンプル。
コードはこちらで動作確認できます。
Installation
Next.js 15、 Shadcn/UIのインストール
# Next.js 15
npx create-next-app@latest [app name]
# Shadcn/UI 公式
npx shadcn@latest add sidebar
# Shadcn/UI 公式 サイドバーコンポーネントのサンプル
# ※このサンプルをインストールする時サンプルが重複する時はすべて上書きをします。(重要)
npx shadcn@latest add sidebar-01
npx shadcn@latest add sidebar-02
npx shadcn@latest add sidebar-03
npx shadcn@latest add sidebar-04
npx shadcn@latest add sidebar-05
npx shadcn@latest add sidebar-06
npx shadcn@latest add sidebar-07
npx shadcn@latest add sidebar-08
npx shadcn@latest add sidebar-09
npx shadcn@latest add sidebar-10
npx shadcn@latest add sidebar-11
npx shadcn@latest add sidebar-12
npx shadcn@latest add sidebar-13
npx shadcn@latest add sidebar-14
npx shadcn@latest add sidebar-15
# Shadcn/UI 自作コードで必要なライブラリ
# 01-
npx shadcn@latest add sidebar
# 07
npx shadcn@latest add dropdown-menu
# 11
npx shadcn@latest add collapsible
shadcn/ui サイドバー用のコンポーネント一覧
<Sidebar>
<SidebarContent>
<SidebarFooter>
<SidebarGroup>
<SidebarGroupAction>
<SidebarGroupContent>
<SidebarGroupLabel>
<SidebarHeader>
<SidebarInput>
<SidebarInset>
<SidebarMenu>
<SidebarMenuAction>
<SidebarMenuBadge>
<SidebarMenuButton>
<SidebarMenuItem>
<SidebarMenuSkeleton>
<SidebarMenuSub>
<SidebarMenuSubButton>
<SidebarMenuSubItem>
<SidebarProvider>
<SidebarRail>
<SidebarSeparator>
<SidebarTrigger>
useSidebar <<React Hooks
公式のサンプル
sidebar-01
メニューをすべて
バージョン変更機能がある
sidebar-02
メニューをすべて
メニューを折りたたむ機能
sidebar-03
divタグの追加
sidebar-04
コンポーネントにカスタムスタイルを適用します。
タイトルの追加
sidebar-05
+折りたたみメニュー機能
SidebarRail は、サイドバーの中で主要なコンテンツを配置するための領域を提供するコンポーネントです。これにより、サイドバー内のアイテムが整理され、ユーザーが簡単にナビゲートできるようになります。
「レール」という言葉は、サイドバーのコンテキストでは、サイドバーの主要な構造部分やコンテンツを配置するための領域を指します。具体的には、サイドバーの中でメニューアイテムやナビゲーションリンクなどが配置される部分です。
sidebar-06
ポップアップメニュー
nav-main.tsx
sidebar-opt-in-form.tsx
の追加
サブスクライブ機能(枠だけ)をメニューに表示
sidebar-07
>折りたたみメニュー機能
メニューをグループ分け
nav-main.tsx
nav-projects.tsx
nav-user.tsx
team-switcher.tsx
の追加
sidebar-08
2個目のメニューの追加
nav-main.tsx
nav-projects.tsx
nav-secondary.tsx
の追加
sidebar-09
アイコンメニュー
メールメニュー
折りたたみ
sidebar-10
3面メニュー
グループ
ポップアップメニュー
nav-actions.tsx
nav-favorites.tsx
nav-main.tsx
nav-secondary.tsx
nav-workspaces.tsx
team-switcher.tsx
sidebar-11
深さ2段メニュー
メニュー内容は普通
sidebar-12
カレンダー
ボタン
calendars.tsx
date-picker.tsx
nav-user.tsx
sidebar-13
Open Dialog
画面全体にメニューが表示されるタイプ
タブレット等の画面が狭いメニュー
画面全体にメニューを表示するタイプ
左側のメニューが固定
右側はフリーエリア
dialog.tsx
settings-dialog.tsx
sidebar-14
右側に開く
ほかは普通?
sidebar-15
左右のメニュー
フルコンボ
自分が試してみたサンプル
Structure
構造
-
Sidebar- サイドバー コンテナー全体。
-
SidebarGroup- 内のセクションSidebarContent。
-
SidebarHeaderそしてSidebarFooter- サイドバーの上部と下部に固定されます。
-
SidebarContent- スクロール可能なコンテンツ。
-
SidebarProvider- 折りたたみ可能な状態を処理します。
-
SidebarTrigger- のトリガーSidebar。開閉します。
sample
リポジトリからインストールをしてリンクボタンを押すと、番号と一致したライブラリを使用したサンプル(XXへのリンク XXXX)が見れます。
(Homeボタンを押せばTOPメニュー(http://localhost:3000/)に戻ります。)
01 サイドバーのサンプル
左サイドメニューを閉じて開くだけです。
左サイドメニューの項目などはありません。
最初は閉じてますが、左上のメニューのアイコンを押すことで
左側にサイドバーが開閉されます。
これが基本形です。
app/layout.tsx
components/app-sidebar.tsx
を用意します。
import { AppSidebar } from "@/components/app-sidebar_01";
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
export default function Layout({ children }: { children: React.ReactNode }) {
// このLayoutファイルではサイドバーのメニュー等を表示、操作するだけです。
// サイドバーの中の機能はapp-sidebar_**.tsxに書かれています。
return (
// Shadcn/UIのサイドバーを使うために必ず必要です。
<SidebarProvider>
{/* サイドバーの中身 */}
<AppSidebar />
<main>
{/* サイドバーのトリガー */}
<SidebarTrigger />
{children}
</main>
</SidebarProvider>
);
}
import {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarHeader,
} from "@/components/ui/sidebar"
export function AppSidebar() {
return (
<Sidebar>
<SidebarHeader />
<SidebarContent>
<SidebarGroup />
<SidebarGroup />
</SidebarContent>
<SidebarFooter />
</Sidebar>
)
}
01のサイドバーはこれで完成です。
02-1 02-2 Your First Sidebar
最初のサイドバー
- 02-1 は01からオプションをすべて除いた最低限のもの。
- 02-2 は全てのオプションを付けたフル装備のもの。
メニュー付きの折りたたみ可能なサイドバー
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { AppSidebar } from "@/components/app-sidebar"
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<SidebarProvider>
<AppSidebar />
<main>
<SidebarTrigger />
{children}
</main>
</SidebarProvider>
)
}
import { Sidebar, SidebarContent } from "@/components/ui/sidebar"
export function AppSidebar() {
return (
<Sidebar>
<SidebarContent />
</Sidebar>
)
}
👆02_01 これは最小限のサイドバー設定です。
👇02_02 👆この最小限のサイドバーにオプション全部盛りしています。
import { Calendar, Home, Inbox, Search, Settings } from "lucide-react"
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "@/components/ui/sidebar"
// Menu items.
const items = [
{
title: "Home",
url: "#",
icon: Home,
},
{
title: "Inbox",
url: "#",
icon: Inbox,
},
{
title: "Calendar",
url: "#",
icon: Calendar,
},
{
title: "Search",
url: "#",
icon: Search,
},
{
title: "Settings",
url: "#",
icon: Settings,
},
]
export function AppSidebar() {
return (
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Application</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{items.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild>
<a href={item.url}>
<item.icon />
<span>{item.title}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
)
}
これで、最初のサイドバーが作成されました。
※asChildとは
asChildを使うと、外側のコンポーネント(親要素)は名前だけを借りて、実際の機能や動作は内側のタグ(子要素)が実行します。
これにより、親要素のスタイルや機能を保持しつつ、異なるHTML要素やカスタムコンポーネントを使用することができます。
例
<Button asChild>
<Link href="/login">Login</Link> {/* Linkを子要素として渡す */}
</Button>
👆このようにasChildプロパティを使うことで、見た目はButtonタグですが、実際にはLinkタグのように、このLink機能が実行されます。
Components
コンポーネント
sidebar.tsxのコンポーネントはコンポーザブルに作られており、提供されたコンポーネントを組み合わせてサイドバーを構築します。
また、DropdownMenuやCollapsible、Dialogなどの他のshadcn/uiコンポーネントともうまく組み合わせることができます。
sidebar.tsxのコードを変更自由に行ってください。
sidebar.tsxを出発点として、あなた独自のメニューを作ってください。
次のセクションでは、各コンポーネントとその使い方について説明します。
幅の変更方法
サイドバーの幅を設定するには、プロパティの--sidebar-widthおよびCSS 変数を使用できます。--sidebar-width-mobilestyle
const SIDEBAR_WIDTH = "16rem"
const SIDEBAR_WIDTH_MOBILE = "18rem"
<SidebarProvider
style={{
"--sidebar-width": "20rem",
"--sidebar-width-mobile": "20rem",
}}
>
<Sidebar />
</SidebarProvider>
Keyboard Shortcut
キーボードショートカット
キーボードショートカットを設定します。
Windows
ctrl+b
const SIDEBAR_KEYBOARD_SHORTCUT = "b"
03 Persisted State
03へのリンク 開閉を記憶
ページ状態の保存
SidebarProvider は、ページのリロードとサーバ側のレンダリングにわたってサイドバーの状態を永続化させます。
サイドバーの現在の状態を保存するためにクッキーを使用します。
サイドバーの状態が変更されると、sidebar:stateという名前のデフォルトのクッキーが現在の開閉状態で設定されます。
このクッキーは、サイドバーの状態を復元するために、その後のページロードで読み込まれます。
Next.jsでサイドバーの状態を保持するには、app/layout.tsxでSidebarProviderを次のように設定します:
import { cookies } from "next/headers"
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"
import { AppSidebar } from "@/components/app-sidebar"
export async function Layout({ children }: { children: React.ReactNode }) {
const cookieStore = await cookies()
const defaultOpen = cookieStore.get("sidebar:state")?.value === "true"
return (
<SidebarProvider defaultOpen={defaultOpen}>
<AppSidebar />
<main>
<SidebarTrigger />
{children}
</main>
</SidebarProvider>
)
}
クッキーの名前は、sidebar.tsxのSIDEBAR_COOKIE_NAME変数を更新することで変更できます。
const SIDEBAR_COOKIE_NAME = "sidebar:state"
Sidebar
サイドバー
import { Sidebar } from "@/components/ui/sidebar"
export function AppSidebar() {
return <Sidebar />
}
👆基本的な使い方。
これらにオプション設定をしていきます。
04 side
左/右 サイドバーの表示位置を変更します。
import { Sidebar } from "@/components/ui/sidebar";
export function AppSidebar() {
// <Sidebar side="left | right" />
return <Sidebar side="right" />;
}
05 variant
variant プロパティを使うと、サイドバーの見た目を変えることができます。
サイドバーには3つのバリアントがあります。
- sidebar: 通常のサイドバーで、主に画面の横に固定されている。
- floating: 浮いているように見えるサイドバーで、他のコンテンツの上に重なる感じです。
- inset: 内側に配置されるサイドバーで、メインコンテンツの周りに収まります。
使うときは、
のように指定します。
で囲みます。
<SidebarProvider>
<Sidebar variant="inset" />
<SidebarInset>
<main>{children}</main>
</SidebarInset>
</SidebarProvider>
// <Sidebar variant="sidebar | floating | inset" />
<Sidebar variant="inset">
sidebar:
デフォルトのサイドバーのバリアントです。
通常のサイドバーのように、コンテンツの左または右側に固定して配置されます。
コンテンツがスクロールしても、サイドバーは同じ位置にいます。
floating:
このバリアントは、サイドバーが画面の上に浮かんで表示されます。
メインコンテンツの上にメニューが重なって表示されるからコンテンツの一部が見えなくなることもあります。
視覚的に際立たせるために使用できます。
inset:
このバリアントでは、サイドバーがメインコンテンツに埋め込まれます。
メインコンテンツは、SidebarInsetコンポーネントでラップする必要があります。
サイドバーとメインコンテンツ間のより緊密な統合を作成するために使用できます。
inset - CSS: カスケーディングスタイルシート | MDN
https://developer.mozilla.org/ja/docs/Web/CSS/inset
それぞれのバリアントは、アプリケーションの特定のニーズやデザインの好みに応じて異なる視覚効果とレイアウトを提供します。
06 collapsible
折りたたみ
import { Sidebar } from "@/components/ui/sidebar"
export function AppSidebar() {
return <Sidebar collapsible="offcanvas | icon | none" />
}
サイドバーの折りたたみ方を決めています。
collapsible プロパティ には、 offcanvas 、 icon 、 none の3つの値を設定できます。
offcanvas: 画面の端からスライドして表示・非表示になる、よくあるサイドバーです。
icon: サイドバーが折りたたまれて、アイコンだけが残されて表示されます。
none: サイドバーは折りたたまれません。常に表示されます。
React Hooks useSidebar
useSidebar はサイドバーを制御するために使用されます。
import { useSidebar } from "@/components/ui/sidebar"
export function AppSidebar() {
const {
state,
open,
setOpen,
openMobile,
setOpenMobile,
isMobile,
toggleSidebar,
} = useSidebar()
}
useSidebar を使うと、サイドバーの状態や動作を簡単に制御できる 便利なプロパティ が使えるようになります。
state: 今のサイドバーの状態がわかります。「展開」してるのか「折りたたまれてる」のか、一目瞭然です。
open: サイドバーが開いているかどうかを教えてくれます。 true なら開いてるし、 false なら閉じています。
setOpen: サイドバーを開いたり閉じたりします、 true を渡せば開き、 false を渡せば閉じます。
openMobile: モバイル版のサイドバーが開いているかどうかを教えてくれます。これも true か false で判断します。
setOpenMobile: モバイル版のサイドバーを開いたり閉じたりします setOpen と同じように、 true か false を渡すだけです。
isMobile: 今使ってるのがモバイル端末かどうかを教えてくれます。モバイル用のレイアウトにする必要があるかどうかの判断に便利です。
toggleSidebar: サイドバーを開いたり閉じたりする 切り替え をしてくれます。いちいち open や setOpen を操作しなくても、これを使えばワンタッチで切り替えられます。
- このようにuseSidebar を使えば、サイドバーを思い通りに動かせるようになります。
useSidebar プロパティ日本語解説
useSidebar
フックは、サイドバーを制御するための様々なプロパティを提供します。これらのプロパティを使用することで、サイドバーの開閉状態、モバイルでの表示、状態の切り替えなどをJavaScriptで操作できます。
プロパティ | 型 | 説明 |
---|---|---|
state |
expanded/collapsed | サイドバーの現在の状態 (展開または折りたたみ) を示します。 |
open |
boolean | サイドバーが開いているかどうかを示す真偽値です。 |
setOpen |
(open: boolean) => void | サイドバーの開閉状態を設定します。引数に true を渡すと開き、 false を渡すと閉じます。 |
openMobile |
boolean | モバイル版のサイドバーが開いているかどうかを示す真偽値です。 |
setOpenMobile |
(open: boolean) => void | モバイル版のサイドバーの開閉状態を設定します。 setOpen と同様です。 |
isMobile |
boolean | 現在のデバイスがモバイル端末かどうかを示す真偽値です。 |
toggleSidebar |
() => void | デスクトップとモバイルの両方で、サイドバーの開閉状態を切り替えます。 |
これらのプロパティは、 useSidebar
フックを呼び出すことで取得できます。
import { useSidebar } from "@/components/ui/sidebar"
export function AppSidebar() {
const {
state,
open,
setOpen,
openMobile,
setOpenMobile,
isMobile,
toggleSidebar,
} = useSidebar()
}
取得したプロパティは、サイドバーの動作をカスタマイズする際に役立ちます。
例えば、 isMobile
プロパティを使用して、モバイル端末でのみ特定のスタイルを適用したり、 toggleSidebar
プロパティを使用して、ボタンクリックでサイドバーの開閉状態を切り替えたりすることができます。
07 SidebarHeader
サイドバーのヘッダー
ヘッダーにドロップダウンメニューが使えます。
app-sidebar_07.tsx
08 SidebarFooter
サイドバーのフッター
サイドバーにスティッキーフッターを追加するには、SidebarFooterコンポーネントを使用します。
次の例では、SidebarFooterにを追加しています。
components/app-sidebar_08.tsx
09 SidebarContent
サイドバーのコンテンツを構成する基本形
つまり、メニューの項目など
SidebarContentコンポーネントは、サイドバーのコンテンツをラップするために使用されます。
ここに、SidebarGroupコンポーネントを追加します。
スクロール可能です。
import { Sidebar, SidebarContent } from "@/components/ui/sidebar"
export function AppSidebar() {
return (
<Sidebar>
<SidebarContent>
<SidebarGroup />
<SidebarGroup />
</SidebarContent>
</Sidebar>
)
}
メニューの構成の基本形です。
で
を囲みます。
10 SidebarGroup
サイドバーのグループ
サイドバー内にセクションを作成します。
ラベル(Application)を作り、+のアイコンを添えます。
サイドバー内のグループはで囲みます。
その中で
でラベルを書きます。
で動作を書きます。
SidebarGroupのContentsを囲みます。
import { Sidebar, SidebarContent, SidebarGroup } from "@/components/ui/sidebar"
import { Plus } from "lucide-react" // プラスアイコンのインポート
export function AppSidebar() {
return (
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Application</SidebarGroupLabel>
<SidebarGroupAction>
<Plus /> <span className="sr-only">Add Project</span>
</SidebarGroupAction>
<SidebarGroupContent></SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
)
}
className="sr-only"は、スクリーンリーダーなどの支援技術向けにテキストを提供するもので、視覚的には表示されません。
11 Collapsible SidebarGroup
折りたたみ可能なサイドバーグループ
グループを折りたたみます。
import {
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
} from "@/components/ui/sidebar";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@radix-ui/react-collapsible";
import { ChevronDown } from "lucide-react";
export function AppSidebar() {
return (
<Collapsible defaultOpen className="group/collapsible">
<SidebarGroup>
<SidebarGroupLabel asChild>
<CollapsibleTrigger>
Help
<ChevronDown className="ml-auto transition-transform group-data-[state=open]/collapsible:rotate-180" />
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarGroupContent />
メニュー項目1
<br />
メニュー項目2
<br />
メニュー項目3
</CollapsibleContent>
</SidebarGroup>
</Collapsible>
)
}
12 SidebarGroupAction
SidebarGroupAction コンポーネントを使用して、SidebarGroup にアクション ボタンを追加します。
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupAction,
SidebarGroupContent,
SidebarGroupLabel,
} from "@/components/ui/sidebar";
import { Plus } from "lucide-react";
export function AppSidebar() {
return (
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel asChild>Projects</SidebarGroupLabel>
<SidebarGroupAction title="Add Project">
<Plus /> <span className="sr-only">Add Project</span>
</SidebarGroupAction>
<SidebarGroupContent >
メニュー項目1
<br />
メニュー項目2
<br />
メニュー項目3
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
)
}
13 SidebarMenu
SidebarMenuコンポーネントは、SidebarGroup内にメニューを構築するために使用されます。
SidebarMenuコンポーネントは、
コンポーネントで構成されています。import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
} from "@/components/ui/sidebar";
const projects = [
{
name: "Project 1",
url: "/project1",
icon: "Activity",
},
{
name: "Project 2",
url: "/project2",
icon: "Activity",
},
{
name: "Project 3",
url: "/project3",
icon: "Activity",
},
];
export function AppSidebar() {
return (
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
);
}
SidebarMenuButton
SidebarMenuButtonコンポーネントは、SidebarMenuItem内のメニューボタンをレンダリングするために使用されます。
Link or Anchor
デフォルトでは、SidebarMenuButtonはボタンをレンダリングしますが、asChildプロパティを使用して、Linkやaタグのような別のコンポーネントをレンダリングすることができます。
<SidebarMenuButton asChild>
<a href="#">Home</a>
</SidebarMenuButton>
Icon and Label
<SidebarMenuButton asChild>
<a href="#">
<Home />
<span>Home</span>
</a>
</SidebarMenuButton>
isActive
<SidebarMenuButton asChild isActive>
<a href="#">Home</a>
</SidebarMenuButton>
14 SidebarMenuAction
SidebarMenuActionコンポーネントは、SidebarMenuItem内のメニューアクションをレンダリングするために使用されます。
このボタンは、SidebarMenuButtonとは独立して動作します。
たとえば、をクリック可能なリンクとして、をボタンとして使用することができます。
import {
Sidebar,
SidebarMenuAction,
SidebarMenuButton,
SidebarMenuItem,
} from "@/components/ui/sidebar";
import { Home, Plus } from "lucide-react";
export function AppSidebar() {
return (
<Sidebar>
<SidebarMenuItem>
<SidebarMenuButton asChild>
<a href="/">
<Home />
<span>Home</span>
</a>
</SidebarMenuButton>
<SidebarMenuAction>
<Plus /> <span className="sr-only">Add Project</span>
</SidebarMenuAction>
</SidebarMenuItem>
</Sidebar>
);
}
15 DropdownMenu
以下は、SidebarMenuActionコンポーネントがDropdownMenuをレンダリングする例です。
<SidebarMenuItem>
<SidebarMenuButton asChild>
<a href="#">
<Home />
<span>Home</span>
</a>
</SidebarMenuButton>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuAction>
<MoreHorizontal />
</SidebarMenuAction>
</DropdownMenuTrigger>
<DropdownMenuContent side="right" align="start">
<DropdownMenuItem>
<span>Edit Project</span>
</DropdownMenuItem>
<DropdownMenuItem>
<span>Delete Project</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
三点リーダーのアイコンを押すと、ドロップダウンメニューが使えます。
16 SidebarMenuSub
SidebarMenuSubコンポーネントは、SidebarMenu内のサブメニューを表示するために使用します。
サブメニュー項目を表示するには、とを使用します。
<SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuSub>
<SidebarMenuSubItem>
<SidebarMenuSubButton />
</SidebarMenuSubItem>
<SidebarMenuSubItem>
<SidebarMenuSubButton />
</SidebarMenuSubItem>
</SidebarMenuSub>
</SidebarMenuItem>
※ベース
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarGroupContent,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "@/components/ui/sidebar";
const projects = [
{
name: "Project 1",
url: "/project1",
icon: "Activity",
},
{
name: "Project 2",
url: "/project2",
icon: "Activity",
},
{
name: "Project 3",
url: "/project3",
icon: "Activity",
},
];
export function AppSidebar() {
return (
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarGroupContent>
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
<SidebarMenuSub>
<SidebarMenuSubItem>
<SidebarMenuSubButton />
</SidebarMenuSubItem>
<SidebarMenuSubItem>
<SidebarMenuSubButton />
</SidebarMenuSubItem>
</SidebarMenuSub>
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
);
}
17 Collapsible SidebarMenu
折りたたみ可能なサイドバーメニュー
SidebarMenuコンポーネントを折りたたみ可能にするには、SidebarMenuコンポーネントとSidebarMenuSubコンポーネントをCollapsibleで囲みます。
<SidebarMenu>
<Collapsible defaultOpen className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton />
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
<SidebarMenuSubItem />
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
</SidebarMenu>
※ベース
import * as React from "react";
import { GalleryVerticalEnd } from "lucide-react";
import {
Sidebar,
SidebarContent,
SidebarGroup,
SidebarHeader,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "@/components/ui/sidebar";
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@radix-ui/react-collapsible";
// This is sample data.
const data = {
navMain: [
{
title: "Getting Started",
url: "#",
items: [
{
title: "Installation",
url: "#",
},
{
title: "Project Structure",
url: "#",
},
],
},
{
title: "Building Your Application",
url: "#",
items: [
{
title: "Routing",
url: "#",
},
{
title: "Data Fetching",
url: "#",
isActive: true,
},
{
title: "Rendering",
url: "#",
},
{
title: "Caching",
url: "#",
},
{
title: "Styling",
url: "#",
},
{
title: "Optimizing",
url: "#",
},
{
title: "Configuring",
url: "#",
},
{
title: "Testing",
url: "#",
},
{
title: "Authentication",
url: "#",
},
{
title: "Deploying",
url: "#",
},
{
title: "Upgrading",
url: "#",
},
{
title: "Examples",
url: "#",
},
],
},
{
title: "API Reference",
url: "#",
items: [
{
title: "Components",
url: "#",
},
{
title: "File Conventions",
url: "#",
},
{
title: "Functions",
url: "#",
},
{
title: "next.config.js Options",
url: "#",
},
{
title: "CLI",
url: "#",
},
{
title: "Edge Runtime",
url: "#",
},
],
},
{
title: "Architecture",
url: "#",
items: [
{
title: "Accessibility",
url: "#",
},
{
title: "Fast Refresh",
url: "#",
},
{
title: "Next.js Compiler",
url: "#",
},
{
title: "Supported Browsers",
url: "#",
},
{
title: "Turbopack",
url: "#",
},
],
},
{
title: "Community",
url: "#",
items: [
{
title: "Contribution Guide",
url: "#",
},
],
},
],
};
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
return (
<Sidebar variant="floating" {...props}>
<SidebarHeader>
<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton size="lg" asChild>
<a href="#">
<div className="flex aspect-square size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
<GalleryVerticalEnd className="size-4" />
</div>
<div className="flex flex-col gap-0.5 leading-none">
<span className="font-semibold">Documentation</span>
<span className="">v1.0.0</span>
</div>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
</SidebarMenu>
</SidebarHeader>
<SidebarContent>
<SidebarGroup>
<SidebarMenu className="gap-2">
{data.navMain.map((item) => (
<Collapsible key={item.title} defaultOpen className="group/collapsible">
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton asChild>
<a href={item.url} className="font-medium">
{item.title}
</a>
</SidebarMenuButton>
</CollapsibleTrigger>
{item.items?.length ? (
<CollapsibleContent>
<SidebarMenuSub className="ml-0 border-l-0 px-1.5">
{item.items.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild isActive={subItem.isActive}>
<a href={subItem.url}>{subItem.title}</a>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
) : null}
</SidebarMenuItem>
</Collapsible>
))}
</SidebarMenu>
</SidebarGroup>
</SidebarContent>
</Sidebar>
);
}
SidebarMenuBadge
SidebarMenuBadge コンポーネントは、SidebarMenuItem 内にバッジをレンダリングするために使用されます。
<SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuBadge>24</SidebarMenuBadge>
</SidebarMenuItem>
18 SidebarMenuSkeleton
SidebarMenuSkeletonコンポーネントは、SidebarMenuのスケルトンをレンダリングするために使用します。
React Server Components、SWRまたはreact-queryを使用するときに、ロード状態を表示するために使用できます。
import { Sidebar, SidebarContent, SidebarMenu, SidebarMenuItem, SidebarMenuSkeleton } from "@/components/ui/sidebar";
export function AppSidebar() {
return (
<Sidebar>
<SidebarContent>
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton />
</SidebarMenuItem>
))}
</SidebarMenu>
</SidebarContent>
</Sidebar>
);
}
SidebarSeparator
サイドバーの中に区切り線を表示します。
<Sidebar>
<SidebarHeader />
<SidebarSeparator />
<SidebarContent>
<SidebarGroup />
<SidebarSeparator />
<SidebarGroup />
</SidebarContent>
</Sidebar>
import { Sidebar, SidebarContent, SidebarSeparator, SidebarGroup, SidebarHeader, } from "@/components/ui/sidebar";
export function AppSidebar() {
return (
<Sidebar>
<SidebarHeader />
<SidebarSeparator />
1
<SidebarSeparator />
2
<SidebarSeparator />
3
<SidebarSeparator />
<SidebarContent>
<SidebarGroup />
<SidebarSeparator />
<SidebarGroup />
</SidebarContent>
</Sidebar>
);
}
19 SidebarTrigger
サイドバーを切り替えるボタンをレンダリングするには、SidebarTriggerコンポーネントを使用します。
SidebarTriggerコンポーネントは、SidebarProvider内で使用する必要があります。
<SidebarProvider>
<Sidebar />
<main>
<SidebarTrigger />
</main>
</SidebarProvider>
import { Sidebar, SidebarContent } from "@/components/ui/sidebar";
export function AppSidebar() {
return (
<Sidebar>
<SidebarContent />
</Sidebar>
);
}
import { AppSidebar } from "@/components/app-sidebar_19";
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<SidebarProvider>
<AppSidebar />
<main>
<SidebarTrigger />
{children}
</main>
</SidebarProvider>
);
}
20 Custom Trigger
カスタムトリガーを作成するには、useSidebarフックを使用します。
import { useSidebar } from "@/components/ui/sidebar"
export function CustomTrigger() {
const { toggleSidebar } = useSidebar()
return <button onClick={toggleSidebar}>Toggle Sidebar</button>
}
"use client";
// 👆必要
import { AppSidebar } from "@/components/app-sidebar_20";
import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
import { useSidebar } from "@/components/ui/sidebar";
function CustomTrigger() {
const { toggleSidebar } = useSidebar();
return (
<button type="button" onClick={toggleSidebar}>
Toggle Sidebar
</button>
);
}
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<SidebarProvider>
<AppSidebar />
<main>
{/* カスタムトリガー */}
<CustomTrigger />
{children}
</main>
</SidebarProvider>
);
}
21 SidebarRail
サイドバー内のレールをレンダリングするために使用されます。 このレールは、サイドバーを切り替えるために使用することができます。
SidebarTriggerがサイドバーの外に配置されるのに対して、
SidebarRailはサイドバー自体の中に配置されるのが大きな違いです。
<Sidebar>
<SidebarHeader />
<SidebarContent>
<SidebarGroup />
</SidebarContent>
<SidebarFooter />
111
<SidebarRail />
222
</Sidebar>
Railとは
サイドバーの表示/非表示を切り替えるボタンを表示するための領域を確保することです。
通常はここに、サイドバーを開閉するためのボタンなどを配置します。
SidebarRail 自体は単にレールを表示するためのコンポーネントなので、具体的なボタンなどの要素は自分で用意する必要があります。
サイトバートリガーとサイドバーレール
なぜ単に閉じる機能だけでなく、SidebarTrigger
やSidebarRail
といった、サイドバーの開閉を操作するための複数の方法が用意されているのか?
これは主に、ユーザーエクスペリエンス(UX)とデザインの柔軟性のためです。それぞれのコンポーネントは、異なる状況やデザイン要件に対応できるように設計されています。
- SidebarTrigger
これは、サイドバーの外側に配置されることを想定しています。
例えば、ヘッダー部分にハンバーガーメニューアイコンとして配置したり、メインコンテンツの上部にボタンとして配置したりします。
これにより、サイドバーが閉じている状態でも、ユーザーは明確に「サイドバーを開ける場所」を認識できます。
特に、モバイルファーストなデザインや、コンテンツを広く表示したい場合に適しています。
- SidebarRail
これは、サイドバーの内側、通常は端に配置されます。
サイドバーが閉じている状態では、細い線やアイコンとして表示され、ユーザーはそれをクリックまたはドラッグすることでサイドバーを開くことができます。
これは、サイドバーが常に部分的に表示されているようなデザイン(例えば、ナビゲーションアイコンだけが表示されている状態)でよく使われます。
デスクトップアプリケーションのような、常に何らかのナビゲーション要素を表示しておきたい場合に適しています。
つまり、
完全に隠れたサイドバーを開く場合はSidebarTrigger
部分的に表示されたサイドバーを展開/格納する場合はSidebarRail
という使い分けが考えられます。
例を挙げると、
スマートフォンのウェブサイト:画面が狭いため、通常はサイドバーを隠しておき、ハンバーガーメニュー(SidebarTrigger
)をクリックして表示するのが一般的です。
デスクトップのウェブアプリケーション:画面が広いため、常にサイドバーの一部(アイコンなど)を表示しておき、それをクリックして展開する(SidebarRail
)のが一般的です。
このように、SidebarTrigger
とSidebarRail
は、それぞれ異なるデザインパターンやユーザーの操作方法に対応するために用意されています。
どちらを使うかは、ウェブサイトやアプリケーションのデザインや要件によって決まります。
また、両方を同時に使うことも可能です。例えば、モバイルではSidebarTrigger
を使い、デスクトップではSidebarRail
を使う、といった実装もできます。
要するに、これらのコンポーネントは、開発者がより柔軟にサイドバーの挙動を制御し、最適なユーザーエクスペリエンスを提供できるようにするためのものです。
Data Fetching
React Server Components
以下は、React Server Componentsを使用してプロジェクトのリストをレンダリングするSidebarMenuコンポーネントの例です。
Skeleton to show loading state.
function NavProjectsSkeleton() {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
22 Server component fetching data.
async function NavProjects() {
const projects = await fetchProjects()
return (
<SidebarMenu>
{projects.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
23 Usage with React Suspense.
function AppSidebar() {
return (
<Sidebar>
<SidebarContent>
<SidebarGroup>
<SidebarGroupLabel>Projects</SidebarGroupLabel>
<SidebarGroupContent>
<React.Suspense fallback={<NavProjectsSkeleton />}>
<NavProjects />
</React.Suspense>
</SidebarGroupContent>
</SidebarGroup>
</SidebarContent>
</Sidebar>
)
}
SWR and React Query
SWRやreact-queryでも同じアプローチが使えます。
function NavProjects() {
const { data, isLoading } = useSWR("/api/projects", fetcher)
if (isLoading) {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
if (!data) {
return ...
}
return (
<SidebarMenu>
{data.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
function NavProjects() {
const { data, isLoading } = useQuery()
if (isLoading) {
return (
<SidebarMenu>
{Array.from({ length: 5 }).map((_, index) => (
<SidebarMenuItem key={index}>
<SidebarMenuSkeleton showIcon />
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
if (!data) {
return ...
}
return (
<SidebarMenu>
{data.map((project) => (
<SidebarMenuItem key={project.name}>
<SidebarMenuButton asChild>
<a href={project.url}>
<project.icon />
<span>{project.name}</span>
</a>
</SidebarMenuButton>
</SidebarMenuItem>
))}
</SidebarMenu>
)
}
Controlled Sidebar
サイドバーを制御するには、openとonOpenChangeプロップを使用します。
このコードは、サイドバーを制御する方法を示しています。open
と onOpenChange
というプロップを使って、サイドバーの開閉状態を管理します。
export function AppSidebar() {
const [open, setOpen] = React.useState(false);
return (
<SidebarProvider open={open} onOpenChange={setOpen}>
<Sidebar />
</SidebarProvider>
);
}
React.useState(false) を使って、サイドバーが開いているかどうかを管理する open
という状態を作ります。
初期値は false
です。
SidebarProvider
コンポーネントに open
と onOpenChange
プロップを渡します。
-
open
はサイドバーが開いているかどうかを示します。 -
onOpenChange
はサイドバーの開閉状態が変わったときに呼ばれる関数です。
これにより、サイドバーの開閉状態を簡単に管理できます。
export function AppSidebar() {
const [open, setOpen] = React.useState(false)
return (
<SidebarProvider open={open} onOpenChange={setOpen}>
<Sidebar />
</SidebarProvider>
)
}
Theming
サイドバーのテーマ設定には次の CSS 変数を使用します。
@layer base {
:root {
--sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%;
--sidebar-primary: 240 5.9% 10%;
--sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 240 4.8% 95.9%;
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
.dark {
--sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%;
--sidebar-primary: 0 0% 98%;
--sidebar-primary-foreground: 240 5.9% 10%;
--sidebar-accent: 240 3.7% 15.9%;
--sidebar-accent-foreground: 240 4.8% 95.9%;
--sidebar-border: 240 3.7% 15.9%;
--sidebar-ring: 217.2 91.2% 59.8%;
}
}
意図的にサイドバーとアプリケーションの残りの部分に異なる変数を使い、アプリケーションの残りの部分とは異なるスタイルのサイドバーを簡単に持てるようにしています。 メインアプリケーションとは異なる暗い色合いのサイドバーを考えてみてください。
Styling
ここでは、異なる状態に基づいたサイドバーのスタイルのヒントをいくつか紹介します。
サイドバーの折りたたみ可能な状態に基づいて要素をスタイリングします。 以下は、サイドバーがアイコンモードのとき、SidebarGroupを非表示にします。
<Sidebar collapsible="icon">
<SidebarContent>
<SidebarGroup className="group-data-[collapsible=icon]:hidden" />
</SidebarContent>
</Sidebar>
メニューボタンのアクティブ状態に応じてメニューアクションをスタイリングする。 以下のようにすると、メニューボタンがアクティブなときにメニューアクションが表示されるようになります。
<SidebarMenuItem>
<SidebarMenuButton />
<SidebarMenuAction className="peer-data-[active=true]/menu-button:opacity-100" />
</SidebarMenuItem>
Changelog
最新ログ
2024-10-30 Cookie handling in setOpen