This is the full developer documentation for Saas UI v3.
# Area
Compose `areaY` for the fill and `lineY` for the edge. Keep both marks on
the same fields so they share scales.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { areaY, lineY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', mrr: 42000 },
{ date: 'Feb', mrr: 45800 },
{ date: 'Mar', mrr: 47600 },
{ date: 'Apr', mrr: 51200 },
{ date: 'May', mrr: 53800 },
{ date: 'Jun', mrr: 58100 },
]
const compactCurrency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
notation: 'compact',
maximumFractionDigits: 1,
})
export const ChartArea = () => {
const chart = useChart({
data,
series: [{ name: 'mrr', label: 'MRR', color: 'indigo.solid' }],
})
const definition = useMemo(
() =>
chart.define({
marks: [
areaY(chart.data, {
x: 'date',
y: 'mrr',
fill: chart.color('indigo.solid'),
fillOpacity: 0.16,
}),
lineY(chart.data, {
x: 'date',
y: 'mrr',
stroke: chart.color('indigo.solid'),
strokeWidth: 2,
}),
],
x: {
scale: () => scaleBand().padding(0.12),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: {
line: false,
ticks: {
size: 0,
format: (value: number) => compactCurrency.format(value),
},
},
},
}),
[chart],
)
return (
)
}
```
## Usage
```tsx
import { Chart, useChart } from '@saas-ui/charts'
import { areaY, lineY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
```
```tsx
const definition = chart.define({
marks: [
areaY(chart.data, {
x: 'date',
y: 'mrr',
fill: chart.color('indigo.solid'),
fillOpacity: 0.16,
}),
lineY(chart.data, {
x: 'date',
y: 'mrr',
stroke: chart.color('indigo.solid'),
strokeWidth: 2,
}),
],
x: { scale: () => scaleBand().padding(0.12) },
y: { scale: scaleLinear, nice: true, grid: true },
})
```
Use `y1` and `y2` on `areaY` for a range or forecast band. `chart.gradient()`
builds token-aware linear gradients when you need a fade instead of a flat
fill.
# Bar List
`BarList` is an HTML composition. It uses the same `useChart` instance, but
lays bars out with Chakra instead of SVG marks. Use it when you want labels
and values in the document flow.
```tsx
'use client'
import { BarList, useChart, type BarListData } from '@saas-ui/charts'
const data: BarListData[] = [
{ name: 'Direct', value: 1240 },
{ name: 'Organic', value: 890 },
{ name: 'Referral', value: 640 },
{ name: 'Social', value: 310 },
]
export const BarListBasic = () => {
const chart = useChart({
data,
series: [{ name: 'name', color: 'indigo.solid' }],
})
return (
)
}
```
## Usage
```tsx
import { BarList, useChart } from '@saas-ui/charts'
```
```tsx
const chart = useChart({
data: [
{ name: 'Direct', value: 1240 },
{ name: 'Organic', value: 890 },
],
series: [{ name: 'name', color: 'indigo.solid' }],
})
```
Each row needs a `name` and a numeric `value`. Set the series `name` to
`"name"` so `BarList.Bar` can read the series color.
## Anatomy
```tsx
```
- `BarList.Bar` draws the bar and optional hover tooltip
- `BarList.Value` prints the compact-formatted number
- `barSize` on `Root` sets the row height from a size token
Pass a function to `BarList.Bar`'s `label` or `tooltip` props to customize
the row content.
## Sorted
Pass `sort` to `useChart` to rank the rows without mutating your source
data.
```tsx
'use client'
import { BarList, useChart, type BarListData } from '@saas-ui/charts'
const data: BarListData[] = [
{ name: 'Referral', value: 640 },
{ name: 'Direct', value: 1240 },
{ name: 'Social', value: 310 },
{ name: 'Organic', value: 890 },
]
export const BarListSorted = () => {
const chart = useChart({
data,
sort: { by: 'value', direction: 'desc' },
series: [{ name: 'name', color: 'teal.solid' }],
})
return (
Traffic
)
}
```
```tsx
const chart = useChart({
data,
sort: { by: 'value', direction: 'desc' },
series: [{ name: 'name', color: 'teal.solid' }],
})
```
# Bar Segment
`BarSegment` splits a single bar into colored segments. Each datum needs a
`name`, `value` and `color`.
```tsx
'use client'
import { BarSegment, useChart } from '@saas-ui/charts'
const data = [
{ name: 'Starter', value: 70, color: 'indigo.solid' },
{ name: 'Pro', value: 40, color: 'pink.solid' },
{ name: 'Enterprise', value: 25, color: 'fg' },
]
export const BarSegmentBasic = () => {
const chart = useChart({ data })
return (
)
}
```
## Usage
```tsx
import { BarSegment, useChart } from '@saas-ui/charts'
```
```tsx
const chart = useChart({
data: [
{ name: 'Starter', value: 70, color: 'indigo.solid' },
{ name: 'Pro', value: 40, color: 'pink.solid' },
{ name: 'Enterprise', value: 25, color: 'fg' },
],
})
```
## Anatomy
```tsx
```
Segment width is the value's share of the total. `BarSegment.Label` and
`BarSegment.Value` use the same flex basis so they line up with the bar.
## Legend
`BarSegment.Legend` lists each segment with a swatch. Set `showValue` and
`showPercent` to print the number and share.
```tsx
'use client'
import { BarSegment, useChart } from '@saas-ui/charts'
const data = [
{ name: 'Starter', value: 70, color: 'indigo.solid' },
{ name: 'Pro', value: 40, color: 'pink.solid' },
{ name: 'Enterprise', value: 25, color: 'fg' },
]
export const BarSegmentWithLegend = () => {
const chart = useChart({ data })
return (
)
}
```
## Reference
`BarSegment.Reference` draws a marker at an absolute value on the same
scale, useful for a quota or limit.
```tsx
'use client'
import { BarSegment, useChart } from '@saas-ui/charts'
const data = [
{ name: 'Used', value: 72, color: 'indigo.solid' },
{ name: 'Available', value: 28, color: 'bg.muted' },
]
export const BarSegmentWithReference = () => {
const chart = useChart({ data })
return (
)
}
```
# Bar
Use TanStack's `barY` mark inside `chart.define()`. Resolve fills with
`chart.color()` so bars follow the active appearance.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { barY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', revenue: 12500 },
{ date: 'Feb', revenue: 15800 },
{ date: 'Mar', revenue: 14200 },
{ date: 'Apr', revenue: 16900 },
{ date: 'May', revenue: 13600 },
{ date: 'Jun', revenue: 19200 },
]
const compactCurrency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
notation: 'compact',
maximumFractionDigits: 1,
})
export const ChartBar = () => {
const chart = useChart({
data,
series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }],
})
const definition = useMemo(
() =>
chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
maxThickness: 20,
radius: 2,
}),
],
x: {
scale: () => scaleBand().padding(0.28),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: {
line: false,
ticks: {
size: 0,
format: (value: number) => compactCurrency.format(value),
},
},
},
}),
[chart],
)
return (
)
}
```
## Usage
```tsx
import { Chart, useChart } from '@saas-ui/charts'
import { barY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
```
```tsx
const definition = chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
maxThickness: 20,
radius: 2,
}),
],
x: {
scale: () => scaleBand().padding(0.28),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
},
})
```
Categorical x values use `scaleBand`. Numeric y values use `scaleLinear`.
`maxThickness` keeps bars from stretching on wide charts; `radius` rounds
the corners.
Use `barX` from `@tanstack/charts` for horizontal bars, and flip the scales
so `y` is the band axis.
## Tooltip
Pass `renderTooltipBody` on `Chart.Root` to format values.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { barY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', revenue: 12500 },
{ date: 'Feb', revenue: 15800 },
{ date: 'Mar', revenue: 14200 },
{ date: 'Apr', revenue: 16900 },
{ date: 'May', revenue: 13600 },
{ date: 'Jun', revenue: 19200 },
]
const currency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
})
const compactCurrency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
notation: 'compact',
maximumFractionDigits: 1,
})
export const ChartTooltip = () => {
const chart = useChart({
data,
series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }],
})
const definition = useMemo(
() =>
chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
maxThickness: 20,
radius: 2,
}),
],
x: {
scale: () => scaleBand().padding(0.28),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: {
line: false,
ticks: {
size: 0,
format: (value: number) => compactCurrency.format(value),
},
},
},
}),
[chart],
)
return (
(
currency.format(Number(value))}
/>
)}
/>
)
}
```
See [bar and rect marks](https://tanstack.com/charts/latest/docs/reference/marks/bar-and-rect)
for stacking, grouping, `y1` / `y2` baselines and the rest of the mark API.
# Chart
`Chart` is the SVG host. Pass it the instance from `useChart` and a
definition from `chart.define()`.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { barY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', revenue: 12500 },
{ date: 'Feb', revenue: 15800 },
{ date: 'Mar', revenue: 14200 },
{ date: 'Apr', revenue: 16900 },
{ date: 'May', revenue: 13600 },
{ date: 'Jun', revenue: 19200 },
]
const compactCurrency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
notation: 'compact',
maximumFractionDigits: 1,
})
export const ChartBar = () => {
const chart = useChart({
data,
series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }],
})
const definition = useMemo(
() =>
chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
maxThickness: 20,
radius: 2,
}),
],
x: {
scale: () => scaleBand().padding(0.28),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: {
line: false,
ticks: {
size: 0,
format: (value: number) => compactCurrency.format(value),
},
},
},
}),
[chart],
)
return (
)
}
```
## Usage
```tsx
import { Chart, useChart } from '@saas-ui/charts'
```
```tsx
```
`Chart.Root` applies the chart CSS variables, sets a landscape aspect ratio
unless you pass `width` and `height`, and renders the default
`Chart.Tooltip` unless you replace it with `renderTooltipBody`.
Always pass `ariaLabel`. TanStack Charts uses it for the accessible name of
the graphic.
## Tooltip
The default tooltip lists the focused points with a color swatch, series
label and formatted value. Override it when you need custom formatting,
totals or a different layout.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { barY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', revenue: 12500 },
{ date: 'Feb', revenue: 15800 },
{ date: 'Mar', revenue: 14200 },
{ date: 'Apr', revenue: 16900 },
{ date: 'May', revenue: 13600 },
{ date: 'Jun', revenue: 19200 },
]
const currency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
})
const compactCurrency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
notation: 'compact',
maximumFractionDigits: 1,
})
export const ChartTooltip = () => {
const chart = useChart({
data,
series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }],
})
const definition = useMemo(
() =>
chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
maxThickness: 20,
radius: 2,
}),
],
x: {
scale: () => scaleBand().padding(0.28),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: {
line: false,
ticks: {
size: 0,
format: (value: number) => compactCurrency.format(value),
},
},
},
}),
[chart],
)
return (
(
currency.format(Number(value))}
/>
)}
/>
)
}
```
```tsx
(
currency.format(Number(value))}
/>
)}
/>
```
`Chart.Tooltip` accepts `title`, `hideLabel`, `hideIndicator`,
`hideSeriesLabel`, `showTotal`, `nameKey` and a `render` function for full
control of each point.
## Legend
Render `Chart.Legend` as a child of `Chart.Root`. It reads `chart.series`
and highlights a series on hover. Set `interaction="click"` to toggle the
highlight instead.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { lineY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', desktop: 820, mobile: 420 },
{ date: 'Feb', desktop: 910, mobile: 670 },
{ date: 'Mar', desktop: 880, mobile: 610 },
{ date: 'Apr', desktop: 1040, mobile: 780 },
{ date: 'May', desktop: 980, mobile: 780 },
{ date: 'Jun', desktop: 1120, mobile: 990 },
]
export const ChartLineMultiple = () => {
const chart = useChart({
data,
series: [
{ name: 'desktop', label: 'Desktop', color: 'indigo.solid' },
{ name: 'mobile', label: 'Mobile', color: 'pink.solid' },
],
})
const definition = useMemo(
() =>
chart.define({
marks: [
lineY(chart.data, {
x: 'date',
y: 'desktop',
stroke: chart.color('indigo.solid'),
strokeWidth: 2,
points: true,
}),
lineY(chart.data, {
x: 'date',
y: 'mobile',
stroke: chart.color('pink.solid'),
strokeWidth: 2,
points: true,
}),
],
x: {
scale: () => scaleBand().padding(0.12),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: { line: false, ticks: { size: 0 } },
},
}),
[chart],
)
return (
)
}
```
```tsx
```
Use `align`, `orientation` and `spacing` to change the layout. The legend
only renders when at least one series has a `name`.
## Center
`Chart.Center` absolutely positions children over the chart. Use it for a
total or KPI in the middle of a donut.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { pie, polar, radialArc } from '@tanstack/charts/polar'
import { Stack, Text } from '@chakra-ui/react'
import { useMemo } from 'react'
const data = [
{ name: 'Starter', value: 42 },
{ name: 'Pro', value: 35 },
{ name: 'Enterprise', value: 23 },
]
export const ChartPieWithCenter = () => {
const chart = useChart({
data,
series: [
{ name: 'Starter', color: 'indigo.solid' },
{ name: 'Pro', color: 'pink.solid' },
{ name: 'Enterprise', color: 'fg' },
],
})
const definition = useMemo(() => {
const slices = pie(chart.data, {
value: 'value',
gapAngle: (2 * Math.PI) / 180,
})
return chart.define({
marks: [
polar({
marks: [
radialArc(slices, {
key: 'name',
color: 'name',
innerRadius: ({ radius }) => radius * 0.68,
stroke: 'none',
}),
],
}),
],
color: {
domain: chart.data.map((item) => item.name),
range: chart.palette,
},
margin: 0,
})
}, [chart])
return (
Customers
{chart.getTotal('value')}
)
}
```
# Line
Use TanStack's `lineY` mark. Map the category to `x` and the metric to `y`,
and stroke with a resolved token.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { lineY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', active: 1240 },
{ date: 'Feb', active: 1580 },
{ date: 'Mar', active: 1490 },
{ date: 'Apr', active: 1820 },
{ date: 'May', active: 1760 },
{ date: 'Jun', active: 2110 },
]
export const ChartLine = () => {
const chart = useChart({
data,
series: [{ name: 'active', label: 'Active users', color: 'indigo.solid' }],
})
const definition = useMemo(
() =>
chart.define({
marks: [
lineY(chart.data, {
x: 'date',
y: 'active',
stroke: chart.color('indigo.solid'),
strokeWidth: 2,
points: true,
}),
],
x: {
scale: () => scaleBand().padding(0.12),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: { line: false, ticks: { size: 0 } },
},
}),
[chart],
)
return (
)
}
```
## Usage
```tsx
import { Chart, useChart } from '@saas-ui/charts'
import { lineY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
```
```tsx
const definition = chart.define({
marks: [
lineY(chart.data, {
x: 'date',
y: 'active',
stroke: chart.color('indigo.solid'),
strokeWidth: 2,
points: true,
}),
],
x: { scale: () => scaleBand().padding(0.12) },
y: { scale: scaleLinear, nice: true, grid: true },
})
```
`points: true` draws a dot at each observation. For time series, swap the x
scale for `scaleUtc` from `d3-scale` and pass `Date` values.
## Multiple series
Add one `lineY` mark per series, or partition a long table with `z`. Declare
matching `series` entries so the tooltip and legend can name each line.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { lineY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', desktop: 820, mobile: 420 },
{ date: 'Feb', desktop: 910, mobile: 670 },
{ date: 'Mar', desktop: 880, mobile: 610 },
{ date: 'Apr', desktop: 1040, mobile: 780 },
{ date: 'May', desktop: 980, mobile: 780 },
{ date: 'Jun', desktop: 1120, mobile: 990 },
]
export const ChartLineMultiple = () => {
const chart = useChart({
data,
series: [
{ name: 'desktop', label: 'Desktop', color: 'indigo.solid' },
{ name: 'mobile', label: 'Mobile', color: 'pink.solid' },
],
})
const definition = useMemo(
() =>
chart.define({
marks: [
lineY(chart.data, {
x: 'date',
y: 'desktop',
stroke: chart.color('indigo.solid'),
strokeWidth: 2,
points: true,
}),
lineY(chart.data, {
x: 'date',
y: 'mobile',
stroke: chart.color('pink.solid'),
strokeWidth: 2,
points: true,
}),
],
x: {
scale: () => scaleBand().padding(0.12),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: { line: false, ticks: { size: 0 } },
},
}),
[chart],
)
return (
)
}
```
```tsx
const chart = useChart({
data,
series: [
{ name: 'desktop', label: 'Desktop', color: 'indigo.solid' },
{ name: 'mobile', label: 'Mobile', color: 'pink.solid' },
],
})
```
# Charts
`@saas-ui/charts` is a Chakra token layer for
[TanStack Charts](https://tanstack.com/charts). It resolves colors, spacing and
typography from your theme, then you compose chart geometry with TanStack
marks.
The package ships the pieces you reuse on every chart, not a catalog of
one-off chart components:
- `useChart` — data, series, token helpers and `define()`
- `Chart` — themed root, tooltip, legend and center overlay
- `BarList` and `BarSegment` — HTML compositions for ranked and part-to-whole
data
## Installation
:::code-group
```bash [npm]
npm i @saas-ui/charts @tanstack/charts
```
```bash [yarn]
yarn add @saas-ui/charts @tanstack/charts
```
```bash [pnpm]
pnpm add @saas-ui/charts @tanstack/charts
```
```bash [bun]
bun add @saas-ui/charts @tanstack/charts
```
:::
`@saas-ui/charts` and `@tanstack/charts` are peer dependencies. Charts also
expect a Chakra / Saas UI provider so token lookups resolve.
## Usage
Create a chart instance with `useChart`, build a TanStack definition with
`chart.define()`, and render it through `Chart.Root`.
```tsx
import { Chart, useChart } from '@saas-ui/charts'
import { barY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
const chart = useChart({
data,
series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }],
})
const definition = chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
}),
],
x: { scale: () => scaleBand().padding(0.28) },
y: { scale: scaleLinear, nice: true, grid: true },
})
return (
)
```
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { barY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', revenue: 12500 },
{ date: 'Feb', revenue: 15800 },
{ date: 'Mar', revenue: 14200 },
{ date: 'Apr', revenue: 16900 },
{ date: 'May', revenue: 13600 },
{ date: 'Jun', revenue: 19200 },
]
const compactCurrency = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
notation: 'compact',
maximumFractionDigits: 1,
})
export const ChartBar = () => {
const chart = useChart({
data,
series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }],
})
const definition = useMemo(
() =>
chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
maxThickness: 20,
radius: 2,
}),
],
x: {
scale: () => scaleBand().padding(0.28),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: {
line: false,
ticks: {
size: 0,
format: (value: number) => compactCurrency.format(value),
},
},
},
}),
[chart],
)
return (
)
}
```
`chart.color()`, `chart.size()` and `chart.spacing()` resolve Saas UI tokens
to CSS values TanStack can paint. Pass token paths such as `indigo.solid` or
`fg.muted`.
## Mental model
TanStack Charts is a grammar of graphics: you pick marks (`barY`, `lineY`,
`areaY`, polar arcs) and map your fields onto channels. `@saas-ui/charts`
does not wrap those marks. It gives you a theme-aware `define()` so the
same chart follows light and dark mode, and Chakra components for tooltip,
legend and HTML chart layouts.
See the [TanStack Charts docs](https://tanstack.com/charts) for the full mark
and scale catalog.
## What's included
| Export | Use it for |
| --- | --- |
| [useChart](/docs/charts/use-chart) | Data, series, formatting, token helpers, `define()` |
| [Chart](/docs/charts/chart) | SVG charts: root, tooltip, legend, center |
| [Bar](/docs/charts/bar), [Line](/docs/charts/line), [Area](/docs/charts/area), [Pie](/docs/charts/pie) | Common mark compositions |
| [Bar List](/docs/charts/bar-list) | Ranked HTML bars |
| [Bar Segment](/docs/charts/bar-segment) | Part-to-whole HTML bars |
# Pie
Pie and donut charts use TanStack's polar marks. `pie()` computes the
angles, `polar()` establishes the layout, and `radialArc()` paints the
slices.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { pie, polar, radialArc } from '@tanstack/charts/polar'
import { useMemo } from 'react'
const data = [
{ name: 'Starter', value: 42 },
{ name: 'Pro', value: 35 },
{ name: 'Enterprise', value: 23 },
]
export const ChartPie = () => {
const chart = useChart({
data,
series: [
{ name: 'Starter', color: 'indigo.solid' },
{ name: 'Pro', color: 'pink.solid' },
{ name: 'Enterprise', color: 'fg' },
],
})
const definition = useMemo(() => {
const slices = pie(chart.data, {
value: 'value',
gapAngle: (2 * Math.PI) / 180,
})
return chart.define({
marks: [
polar({
marks: [
radialArc(slices, {
key: 'name',
color: 'name',
stroke: 'none',
}),
],
}),
],
color: {
domain: chart.data.map((item) => item.name),
range: chart.palette,
},
margin: 0,
})
}, [chart])
return (
)
}
```
## Usage
```tsx
import { Chart, useChart } from '@saas-ui/charts'
import { pie, polar, radialArc } from '@tanstack/charts/polar'
```
```tsx
const chart = useChart({
data,
series: [
{ name: 'Starter', color: 'indigo.solid' },
{ name: 'Pro', color: 'pink.solid' },
{ name: 'Enterprise', color: 'fg' },
],
})
const slices = pie(chart.data, {
value: 'value',
gapAngle: (2 * Math.PI) / 180,
})
const definition = chart.define({
marks: [
polar({
marks: [
radialArc(slices, {
key: 'name',
color: 'name',
stroke: 'none',
}),
],
}),
],
color: {
domain: chart.data.map((item) => item.name),
range: chart.palette,
},
margin: 0,
})
```
Series colors become `chart.palette`. Point the categorical color scale at
that palette so slices stay in sync with the legend.
## Donut
Set `innerRadius` on `radialArc` and overlay `Chart.Center` for a total.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { pie, polar, radialArc } from '@tanstack/charts/polar'
import { Stack, Text } from '@chakra-ui/react'
import { useMemo } from 'react'
const data = [
{ name: 'Starter', value: 42 },
{ name: 'Pro', value: 35 },
{ name: 'Enterprise', value: 23 },
]
export const ChartPieWithCenter = () => {
const chart = useChart({
data,
series: [
{ name: 'Starter', color: 'indigo.solid' },
{ name: 'Pro', color: 'pink.solid' },
{ name: 'Enterprise', color: 'fg' },
],
})
const definition = useMemo(() => {
const slices = pie(chart.data, {
value: 'value',
gapAngle: (2 * Math.PI) / 180,
})
return chart.define({
marks: [
polar({
marks: [
radialArc(slices, {
key: 'name',
color: 'name',
innerRadius: ({ radius }) => radius * 0.68,
stroke: 'none',
}),
],
}),
],
color: {
domain: chart.data.map((item) => item.name),
range: chart.palette,
},
margin: 0,
})
}, [chart])
return (
Customers
{chart.getTotal('value')}
)
}
```
```tsx
radialArc(slices, {
key: 'name',
color: 'name',
innerRadius: ({ radius }) => radius * 0.68,
stroke: 'none',
})
```
# useChart
`useChart` is the single hook for every chart in `@saas-ui/charts`. It stores
the data, resolves Chakra tokens, and returns helpers used by `Chart.Root`,
`BarList` and `BarSegment`.
```tsx
import { useChart } from '@saas-ui/charts'
const chart = useChart({
data,
series: [{ name: 'revenue', label: 'Revenue', color: 'indigo.solid' }],
})
```
## Options
| Prop | Type | Description |
| --- | --- | --- |
| `data` | `T[]` | Rows the marks and compositions read from. |
| `series` | `SeriesItem[]` | Named series for color, label, icon and tooltip matching. |
| `sort` | `{ by, direction }` | Sort a copy of `data` by a numeric field. |
### Series
```tsx
interface SeriesItem {
name?: keyof T | string
color?: ChartColor
icon?: React.ReactNode
label?: React.ReactNode
}
```
`name` is matched against mark and tooltip payloads. `color` accepts a token
path (`indigo.solid`) or any CSS color. When series omit colors, `useChart`
falls back to `indigo.solid`, `pink.solid`, `teal.solid`, `orange.solid`,
`purple.solid` and `fg`.
## Return value
### Data
- `data` — the original rows, or a sorted copy when `sort` is set
- `series` — the series you passed in
- `groupBy(dataKey)` — group rows by a field
- `getSeries(item)` — resolve the series for a tooltip point or datum
- `getTotal(dataKey)`, `getMin(dataKey)`, `getMax(dataKey)`
- `getValuePercent(dataKey, value, domain?)` — percent of the total, or of a
custom `[min, max]` domain
- `getPayloadTotal(payload)` — sum tooltip point values
### Tokens
- `color(token)`, `size(token)`, `spacing(token)` — resolve theme tokens
- `theme` — TanStack theme (`foreground`, `muted`, `grid`, `background`,
`palette`)
- `palette` — resolved series colors, used by pie and categorical scales
- `cssVars` — CSS variables applied by `Chart.Root` for tooltip chrome and
series colors
- `gradient({ id, stops })` — build a linear gradient with token colors
### Definition
`chart.define(spec)` wraps TanStack's `defineChart`. It injects the theme,
disables SVG animation by default, and portals tooltips so they are not
clipped by overflow.
```tsx
const definition = chart.define({
marks: [
barY(chart.data, {
x: 'date',
y: 'revenue',
fill: chart.color('indigo.solid'),
}),
],
x: { scale: () => scaleBand().padding(0.28) },
y: { scale: scaleLinear, nice: true, grid: true },
})
```
Pass any TanStack chart spec: marks, scales, axes, `color`, `margin`,
`gradients` or a responsive `chart({ width })` builder. See
[chart definitions](https://tanstack.com/charts/latest/docs/concepts/chart-definitions)
in the TanStack docs.
### Formatting
- `formatNumber(options?)` — `Intl.NumberFormat` bound to the Chakra locale
- `formatDate(options?)` — `Intl.DateTimeFormat` bound to the Chakra locale
### Highlighting
- `highlightedSeries` / `setHighlightedSeries(name)`
- `isHighlightedSeries(name)`
- `getSeriesOpacity(name, fallback?)` — `1` for the highlighted series,
`fallback` (default `0.2`) for the others
`Chart.Legend` uses these to dim sibling series on hover or click. HTML
compositions such as `BarList` and `BarSegment` use the same state for
tooltips.
```tsx
'use client'
import { Chart, useChart } from '@saas-ui/charts'
import { lineY } from '@tanstack/charts'
import { scaleBand } from '@tanstack/charts/scales/band'
import { scaleLinear } from '@tanstack/charts/scales/linear'
import { useMemo } from 'react'
const data = [
{ date: 'Jan', desktop: 820, mobile: 420 },
{ date: 'Feb', desktop: 910, mobile: 670 },
{ date: 'Mar', desktop: 880, mobile: 610 },
{ date: 'Apr', desktop: 1040, mobile: 780 },
{ date: 'May', desktop: 980, mobile: 780 },
{ date: 'Jun', desktop: 1120, mobile: 990 },
]
export const ChartLineMultiple = () => {
const chart = useChart({
data,
series: [
{ name: 'desktop', label: 'Desktop', color: 'indigo.solid' },
{ name: 'mobile', label: 'Mobile', color: 'pink.solid' },
],
})
const definition = useMemo(
() =>
chart.define({
marks: [
lineY(chart.data, {
x: 'date',
y: 'desktop',
stroke: chart.color('indigo.solid'),
strokeWidth: 2,
points: true,
}),
lineY(chart.data, {
x: 'date',
y: 'mobile',
stroke: chart.color('pink.solid'),
strokeWidth: 2,
points: true,
}),
],
x: {
scale: () => scaleBand().padding(0.12),
grid: false,
axis: { line: false, ticks: { size: 0 } },
},
y: {
scale: scaleLinear,
nice: true,
grid: true,
axis: { line: false, ticks: { size: 0 } },
},
}),
[chart],
)
return (
)
}
```
# App Shell
## Anatomy
```ts
import { AppShell } from '#components/ui/app-shell'
```
```tsx
}
sidebar={}
aside={}
footer={}
>
{/* Main content */}
```
## Usage
The AppShell component provides a structured layout container that helps you
build consistent application interfaces. It manages the composition and
positioning of key UI elements like navbars and sidebars.
The `header` and `footer` slots span the full width of the shell. The
`sidebar` (left) and `aside` (right) slots are rendered next to the main
content area, which contains the children and scrolls independently.
AppShell fits itself into the viewport by default, using `height="100dvh"`.
Set the `height` prop to constrain it, or use the `fullscreen` prop to fix it
to the viewport instead.
### Navbar layout
Use the `header` slot to render a [Navbar](/docs/components/navbar) above the
main content.
### Sidebar layout
Use the `sidebar` slot to render a [Sidebar](/docs/components/sidebar) next to
the main content. The sidebar requires a `Sidebar.Provider` around the shell
to manage its state.
### Aside
Use the `aside` slot to render a secondary [Aside](/docs/components/aside)
panel on the right side of the main content, for example to show details of a
selected item.
### Fullscreen
Use the `fullscreen` prop to fix the shell to the viewport. This prevents
scroll bouncing of the entire page, giving your app a native feel.
```tsx
{/* Main content */}
```
## Props
### AppShell
# Aside
```tsx
'use client'
import { Flex, Text } from '@chakra-ui/react'
import { Aside } from 'compositions/ui/aside'
export const AsideBasic = () => {
return (
Main contentDetailsAside content
)
}
```
## Anatomy
```ts
import { Aside } from '#components/ui/aside'
```
```tsx
Details
```
## Examples
### Controlled
Control open state with `open` and `onOpenChange`.
```tsx
'use client'
import * as React from 'react'
import { Button, Flex, Text } from '@chakra-ui/react'
import { Aside } from 'compositions/ui/aside'
export const AsideControlled = () => {
const [open, setOpen] = React.useState(true)
return (
Main content setOpen(open)}
width="240px"
borderLeftWidth="1px"
bg="bg.panel"
>
DetailsAside content
)
}
```
## Props
### Root
# Navbar
## Anatomy
```jsx
import { Navbar } from '#components/ui/navbar'
```
```jsx
Home
```
## Usage
### Sticky navbar
Navbar uses `position` static by default, you can use `sticky` to make it stick
to the top of the page.
### Border bottom
Simply add `borderBottomWidth="1px"` to the navbar to add a bottom border.
### Glass variant
The `glass` variant will make the background translucent with a blur effect.
### Solid variant
The `solid` variant renders the navbar with solid background color.
### Hide on scroll
Setting the `shouldHideOnScroll` prop to `true` will hide the navbar when the
user scrolls down and show it when the user scrolls up. Use the `parentRef` prop
to specify the parent scroll container, defaults to the `window` object.
### With mobile navigation
Add a mobile navigation menu to the navbar using the `Drawer` component.
# Page
```tsx
'use client'
import { Page } from 'compositions/ui/page'
import { Text } from '@chakra-ui/react'
export const PageBasic = () => {
return (
Page content goes here.
)
}
```
## Anatomy
```ts
import { Page } from '#components/ui/page'
```
```tsx
```
## Examples
### With actions
Add toolbar actions to the page header. Use the `xs` size for buttons in the
header toolbar.
```tsx
'use client'
import { ButtonGroup, Text } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Page } from 'compositions/ui/page'
export const PageWithActions = () => {
return (
}
/>
Page content goes here.
)
}
```
### With filters
Use the `footer` prop on `Page.Header` to render a [filter bar](/docs/components/filters)
below the page title.
```tsx
'use client'
import {
Badge,
Box,
ButtonGroup,
HStack,
Stack,
Text,
} from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { createFilters } from 'compositions/ui/filters'
import { Page } from 'compositions/ui/page'
import { LuLayers } from 'react-icons/lu'
import { z } from 'zod'
interface Account {
id: string
company: string
plan: 'Enterprise' | 'Growth' | 'Starter'
status: 'Active' | 'Trial' | 'Paused'
}
const accounts: Account[] = [
{ id: '1', company: 'Northstar Labs', plan: 'Enterprise', status: 'Active' },
{ id: '2', company: 'Kite & Harbor', plan: 'Growth', status: 'Trial' },
{ id: '3', company: 'Arcwell Health', plan: 'Enterprise', status: 'Active' },
{ id: '4', company: 'Fieldnote Studio', plan: 'Starter', status: 'Paused' },
{ id: '5', company: 'Copperline', plan: 'Growth', status: 'Active' },
{ id: '6', company: 'Daybreak Energy', plan: 'Enterprise', status: 'Active' },
]
const statusDot = (color: string) => (
)
const accountFilters = createFilters({
fields: {
status: {
type: 'enum',
label: 'Status',
schema: z.enum(['Active', 'Trial', 'Paused']),
operators: ['equals', 'not', 'in'],
defaultOperator: 'equals',
meta: { icon: statusDot('border.emphasized'), pluralLabel: 'statuses' },
options: [
{
value: 'Active',
label: 'Active',
meta: { icon: statusDot('green.solid') },
},
{
value: 'Trial',
label: 'Trial',
meta: { icon: statusDot('blue.solid') },
},
{
value: 'Paused',
label: 'Paused',
meta: { icon: statusDot('gray.solid') },
},
],
},
plan: {
type: 'enum',
label: 'Plan',
schema: z.enum(['Enterprise', 'Growth', 'Starter']),
operators: ['equals', 'not', 'in'],
defaultOperator: 'equals',
meta: { icon: , pluralLabel: 'plans' },
options: [
{ value: 'Enterprise', label: 'Enterprise' },
{ value: 'Growth', label: 'Growth' },
{ value: 'Starter', label: 'Starter' },
],
},
},
})
function statusColor(status: Account['status']) {
if (status === 'Active') return 'green'
if (status === 'Trial') return 'blue'
return 'gray'
}
export const PageWithFilters = () => {
const conditions = accountFilters.useConditions()
const matches = conditions.useFilter(accounts)
return (
}
footer={
}
/>
{matches.map((account) => (
{account.company}
{account.plan}
{account.status}
))}
{!matches.length ? (
No accounts match these filters.
) : null}
)
}
```
### With data table
Combine the page layout with a [data table](/docs/components/data-table) to
build list views.
```tsx
'use client'
import { ButtonGroup } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import {
createDataTableColumnHelper,
useDataTable,
} from 'compositions/ui/data-table'
import { Page } from 'compositions/ui/page'
interface Account {
id: string
company: string
plan: 'Enterprise' | 'Growth' | 'Starter'
status: 'Active' | 'Trial' | 'Paused'
mrr: number
}
const accounts: Account[] = [
{
id: '1',
company: 'Northstar Labs',
plan: 'Enterprise',
status: 'Active',
mrr: 12400,
},
{
id: '2',
company: 'Kite & Harbor',
plan: 'Growth',
status: 'Trial',
mrr: 3200,
},
{
id: '3',
company: 'Arcwell Health',
plan: 'Enterprise',
status: 'Active',
mrr: 9800,
},
{
id: '4',
company: 'Fieldnote Studio',
plan: 'Starter',
status: 'Paused',
mrr: 890,
},
{
id: '5',
company: 'Copperline',
plan: 'Growth',
status: 'Active',
mrr: 4700,
},
{
id: '6',
company: 'Daybreak Energy',
plan: 'Enterprise',
status: 'Active',
mrr: 15100,
},
]
const statusPalette = (value: string) =>
value === 'Active' ? 'green' : value === 'Trial' ? 'blue' : 'gray'
const columnHelper = createDataTableColumnHelper()
const columns = columnHelper.columns([
columnHelper.accessor('company', {
header: 'Account',
cell: ({ cell }) => ,
size: 220,
sortFn: 'text',
}),
columnHelper.accessor('plan', {
header: 'Plan',
cell: ({ cell }) => ,
size: 140,
}),
columnHelper.accessor('status', {
header: 'Status',
cell: ({ cell }) => ,
size: 140,
}),
columnHelper.accessor('mrr', {
header: 'MRR',
cell: ({ cell }) => (
),
meta: { isNumeric: true },
size: 140,
}),
])
export const PageWithDataTable = () => {
const table = useDataTable({
columns,
data: accounts,
getRowId: (row) => row.id,
initialState: {
sorting: [{ id: 'company', desc: false }],
},
})
return (
}
/>
)
}
```
### Settings variant
Use the `settings` variant for settings-style pages with larger headings. Pair
the title with the `description` prop for a subtitle.
```tsx
'use client'
import { Text } from '@chakra-ui/react'
import { Page } from 'compositions/ui/page'
export const PageSettings = () => {
return (
Settings content goes here.
)
}
```
### Loading
Set `loading` on `Page.Root` to show a loading overlay in the body.
```tsx
'use client'
import { Text } from '@chakra-ui/react'
import { Page } from 'compositions/ui/page'
export const PageLoading = () => {
return (
This content is hidden while loading.
)
}
```
## Props
### Root
### Header
# Sidebar
```tsx
'use client'
import { Badge, Spacer } from '@chakra-ui/react'
import { SaasUIIcon } from '@saas-ui/assets'
import { IconButton } from 'compositions/ui/icon-button'
import { Menu } from 'compositions/ui/menu'
import { Persona } from 'compositions/ui/persona'
import { Sidebar } from 'compositions/ui/sidebar'
import {
LuFolder,
LuHouse,
LuInbox,
LuSettings,
LuUsers,
} from 'react-icons/lu'
export const SidebarBasic = () => {
return (
Sign out Dashboard
Inbox
12 Contacts
Projects Website redesign
Mobile app
Settings
)
}
```
## Anatomy
```ts
import { Sidebar } from '@/components/ui/sidebar'
```
```tsx
NavigationHome
```
## Usage
### App shell
Combine the sidebar with the `AppShell` component to create a typical
application layout. The sidebar collapses automatically on mobile and renders
a backdrop overlay.
### Inset variant
Set `variant="inset"` on `Sidebar.Provider` and wrap your main content in
`Sidebar.Inset` to render it as a raised panel on top of the sidebar
background.
### Grouped navigation
Use `Sidebar.Group` with a `GroupTitle` to organize navigation into sections,
for example workspace navigation and settings. Combine `Spacer` and `Badge`
to show counts next to an item.
### Collapsible groups
Wrap a group with the `Collapsible` component to let users collapse sections
they use less often. Use `Sidebar.GroupEndElement` for contextual actions like
creating a new project, shown on hover.
### Nested navigation
Nav items can be nested one level deep, for example to show pages inside a
project. Wrap the parent `NavItem` with `Collapsible` and indent the nested
`GroupContent`.
### Hover actions
Use `Sidebar.NavButtonEndElement` to add secondary actions to a nav item,
like removing an item from favourites. Reveal them on hover with the
`_parentHover` condition to keep the sidebar clean.
### User menu
Use `Sidebar.Footer` for content that sticks to the bottom of the sidebar,
like an account menu with profile and sign out actions.
### Toggling the sidebar
`Sidebar.Trigger` toggles the sidebar open and closed. Render one in the
sidebar header to collapse it and one in your page header to re-open it. The
`_open` condition can be used to hide the trigger while the sidebar is open.
### Flyout mode
In `flyout` mode the sidebar stays hidden and flies out over your content
when the cursor reaches the edge of the screen, similar to Notion or Linear.
Render a `Sidebar.FlyoutTrigger` to activate it and a `Sidebar.Backdrop` to
close it when the cursor leaves. Flyout mode is only available on desktop, on
mobile the sidebar falls back to `collapsible` mode.
### Controlled
Control the open state yourself with the `open` and `onOpenChange` props on
`Sidebar.Provider`, for example to persist the state or toggle the sidebar
from elsewhere in your app. Alternatively you can use the `useSidebar` hook
inside the provider to access `open`, `setOpen` and `toggle`.
## Props
### Provider
| Prop | Default | Description |
| -------------- | --------------- | ---------------------------------------------------------------------------------- |
| `mode` | `'collapsible'` | `'collapsible' \| 'flyout'` — Flyout mode is only available on desktop. |
| `variant` | `'sidebar'` | `'sidebar' \| 'inset'` — Inset renders the main content as a raised panel. |
| `defaultOpen` | `true` | The initial visibility of the sidebar. |
| `open` | | Controlled visibility of the sidebar. |
| `onOpenChange` | | Callback invoked when the visibility changes. |
| `onModeChange` | | Callback invoked when the mode changes. |
### NavItem
| Prop | Default | Description |
| -------- | ------- | -------------------------------------------------- |
| `size` | `'md'` | `'sm' \| 'md' \| 'lg'` — The size of the nav item. |
| `active` | `false` | Set on `NavButton` to mark the current page. |
# Split Page
```tsx
'use client'
import { Text } from '@chakra-ui/react'
import { EmptyState } from 'compositions/ui/empty-state'
import { Page } from 'compositions/ui/page'
import { SplitPage } from 'compositions/ui/split-page'
import { LuInbox } from 'react-icons/lu'
export const SplitPageBasic = () => {
return (
Select an item}
title="Inbox zero"
description="Nothing to do here"
/>
)
}
```
## Anatomy
```ts
import { SplitPage, useSplitPage } from '#components/ui/split-page'
```
```tsx
{/* list */}{/* detail */}
```
## Examples
### With content
Combine SplitPage with Page for a full master-detail view.
```tsx
'use client'
import { Text } from '@chakra-ui/react'
import { Page } from 'compositions/ui/page'
import { SplitPage } from 'compositions/ui/split-page'
export const SplitPageWithContent = () => {
return (
Elliot Alderson
Detail content for the selected conversation.
)
}
```
### Responsive
On smaller screens, the detail pane overlays the list. Use `useSplitPage` to
open and close it.
```tsx
'use client'
import { Button, Text } from '@chakra-ui/react'
import { Page } from 'compositions/ui/page'
import { SplitPage, useSplitPage } from 'compositions/ui/split-page'
const DetailPane = () => {
const { onClose } = useSplitPage()
return (
Close
}
/>
Detail content
)
}
const ListPane = () => {
const { onOpen } = useSplitPage()
return (
)
}
export const SplitPageResponsive = () => {
return (
)
}
```
## Props
### SplitPage
# Button
```tsx
'use client'
import { Button } from '@chakra-ui/react'
export const ButtonBasic = () => {
return
}
```
## Anatomy
```jsx
import { Button } from '@chakra-ui/react'
import { ButtonGroup } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the button. You can set the value to
`xs`, `sm`, `md`, or `lg`.
```tsx
'use client';
import { HStack, Button } from '@chakra-ui/react'
export const ButtonWithSizes = () => {
return (
)
}
```
### Variants
Use the `variant` prop to change the visual style of the Button. You can set the
value to `solid`, `subtle`, `outline`, `ghost` or `plain`
```tsx
'use client';
import { HStack } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
export const ButtonWithVariants = () => {
return (
)
}
```
### Icon
Use icons within a button
```tsx
'use client';
import { HStack, Button } from '@chakra-ui/react'
import { RiArrowRightLine, RiMailLine } from 'react-icons/ri'
export const ButtonWithIcons = () => {
return (
)
}
```
### Color
Use the `colorPalette` prop to change the color of the button
```tsx
'use client';
import { Stack, Text } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from '../lib/color-palettes'
export const ButtonWithColors = () => {
return (
{["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"].map((colorPalette) => (
{colorPalette}
))}
)
}
```
### Loading
Use the `loading` and `loadingText` prop to show a loading spinner
```tsx
'use client';
import { Stack, Button } from '@chakra-ui/react'
export const ButtonWithLoading = () => {
return (
)
}
```
### ButtonGroup
Use the `ButtonGroup` component to group buttons together
```tsx
'use client';
import { Button, ButtonGroup } from '@chakra-ui/react'
import { IconButton } from 'compositions/ui/icon-button'
import { LuChevronDown } from 'react-icons/lu'
export const ButtonWithGroup = () => {
return (
)
}
```
## Props
# Close Button
```tsx
'use client'
import { CloseButton } from 'compositions/ui/close-button'
export const CloseButtonBasic = () => {
return
}
```
## Anatomy
```jsx
import { CloseButton } from '#components/ui/close-button'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the close button.
```tsx
'use client'
import { For, HStack } from '@chakra-ui/react'
import { CloseButton } from 'compositions/ui/close-button'
export const CloseButtonWithSizes = () => {
return (
{(size) => }
)
}
```
### Variants
Use the `variant` prop to change the appearance of the close button.
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { CloseButton } from 'compositions/ui/close-button'
export const CloseButtonWithVariants = () => {
return (
)
}
```
### Custom Icon
Use the `children` prop to change the icon of the close button.
```tsx
'use client'
import { CloseButton } from 'compositions/ui/close-button'
import { RiCloseLine } from 'react-icons/ri'
export const CloseButtonWithCustomIcon = () => {
return (
)
}
```
## Props
# Download Trigger
```tsx
'use client'
import { Button, DownloadTrigger } from '@chakra-ui/react'
import { LuDownload } from 'react-icons/lu'
const data = 'The quick brown fox jumps over the lazy dog'
export const DownloadTriggerBasic = () => {
return (
)
}
```
## Anatomy
```jsx
import { DownloadTrigger } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Basic
Pass the `data`, `fileName` and `mimeType` props to the `DownloadTrigger`
component to trigger a download of the data. Use the `asChild` prop to render a
custom trigger, like a `Button`.
```tsx
'use client'
import { Button, DownloadTrigger } from '@chakra-ui/react'
import { LuDownload } from 'react-icons/lu'
const data = 'The quick brown fox jumps over the lazy dog'
export const DownloadTriggerBasic = () => {
return (
)
}
```
### Promise
The `data` prop also accepts a function that returns a `Promise`. The promise
is resolved before the download starts, which is useful for downloading data
that is generated or fetched on demand.
```tsx
'use client'
import { Button, DownloadTrigger } from '@chakra-ui/react'
import { LuDownload } from 'react-icons/lu'
const getData = async () => {
return JSON.stringify({ name: 'Saas UI', type: 'design-system' }, null, 2)
}
export const DownloadTriggerWithPromise = () => {
return (
)
}
```
## Props
# Icon Button
```tsx
import { IconButton } from "@chakra-ui/react"
import { LuSearch } from "react-icons/lu"
export const IconButtonBasic = () => {
return (
)
}
```
## Anatomy
```jsx
import { IconButton } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the icon button. You can set the
value to `xs`, `sm`, `md`, or `lg`.
```tsx
import { For, HStack, IconButton, Text, VStack } from "@chakra-ui/react"
import { LuPhone } from "react-icons/lu"
export const IconButtonWithSizes = () => {
return (
{(size) => (
{size}
)}
)
}
```
### Variants
Use the `variant` prop to change the visual style of the icon button. You can
set the value to `solid`, `subtle`, `surface`, `outline` or `ghost`.
```tsx
import { For, HStack, IconButton, Text, VStack } from "@chakra-ui/react"
import { LuVoicemail } from "react-icons/lu"
export const IconButtonWithVariants = () => {
return (
{(variant) => (
{variant}
)}
)
}
```
### Color
Use the `colorPalette` prop to change the color of the icon button.
```tsx
import { For, HStack } from '@chakra-ui/react'
import { IconButton } from 'compositions/ui/icon-button'
import { LuSearch } from 'react-icons/lu'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from '../lib/color-palettes'
export const IconButtonWithColors = () => {
return (
{(c) => (
)}
)
}
```
### Rounded
Use the `rounded="full"` prop to make the icon button fully rounded.
```tsx
import { IconButton } from "@chakra-ui/react"
import { LuVoicemail } from "react-icons/lu"
export const IconButtonRounded = () => {
return (
)
}
```
## Props
# Badge
```tsx
import { Stack, Badge } from '@chakra-ui/react'
export const BadgeBasic = () => {
return (
DefaultSuccessRemovedNew
)
}
```
## Anatomy
```jsx
import { Badge } from '@chakra-ui/react'
```
```jsx
Badge
```
## Examples
### Icon
Render an icon within the badge directly
```tsx
import { Stack, Badge } from '@chakra-ui/react'
import { HiAtSymbol, HiStar } from 'react-icons/hi'
export const BadgeWithIcon = () => {
return (
New
New
)
}
```
### Variants
Badges come in different variants
```tsx
import { Stack, Badge } from '@chakra-ui/react'
export const BadgeWithVariants = () => {
return (
OutlineSolidSubtleSurface
Outline
Solid
Subtle
Surface
)
}
```
### Sizes
Badges come in different sizes
```tsx
import { HStack, Badge } from '@chakra-ui/react'
export const BadgeWithSizes = () => {
return (
NewNewNewNew
)
}
```
### Colors
Badges come in different colors
```tsx
import { Stack, Text, Badge } from '@chakra-ui/react'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from '../lib/color-palettes'
export const BadgeWithColors = () => {
return (
{["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"].map((colorPalette) => (
{colorPalette}
New
New
New
New
))}
)
}
```
## Props
# Card
```tsx
'use client';
import { Avatar } from 'compositions/ui/avatar'
import { Button, Card } from '@chakra-ui/react'
export const CardBasic = () => {
return (
Nue Camp
This is the card body. Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Curabitur nec odio vel dui euismod fermentum.
Curabitur nec odio vel dui euismod fermentum.
)
}
```
## Anatomy
```jsx
import { Card } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Variants
Use the `variant` prop to change the visual style of the Card.
```tsx
'use client'
import { For, Stack, Button, Card } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
export const CardWithVariants = () => {
return (
{(variant) => (
Nue Camp
This is the card body. Lorem ipsum dolor sit amet, consectetur
adipiscing elit.
)}
)
}
```
### Within Form
Use the Card component within a form to group related fields together.
```tsx
'use client'
import { Card, Stack } from '@chakra-ui/react'
import { Form, useAppForm } from 'compositions/components/forms'
export const CardWithForm = () => {
const form = useAppForm({
defaultValues: {
firstName: '',
lastName: '',
},
onSubmit: ({ value }) => console.log(value),
})
return (
)
}
```
### Sizes
Use the `size` prop to change the size of the Card.
```tsx
'use client';
import { Heading, Stack, Card } from '@chakra-ui/react'
export const CardWithSizes = () => {
return (
Card - sm
This is the card body. Lorem ipsum dolor sit amet, consectetur
adipiscing elit.
Card - md
This is the card body. Lorem ipsum dolor sit amet, consectetur
adipiscing elit.
Card - lg
This is the card body. Lorem ipsum dolor sit amet, consectetur
adipiscing elit.
)
}
```
### With Image
Use the Card component to display an image.
```tsx
'use client';
import { Image, Text, Button, Card } from '@chakra-ui/react'
export const CardWithImage = () => {
return (
Living room Sofa
This sofa is perfect for modern tropical spaces, baroque inspired
spaces.
$450
)
}
```
### Horizontal
Use the Card component to display content horizontally.
```tsx
'use client';
import { Box, HStack, Image, Badge, Button, Card } from '@chakra-ui/react'
export const CardHorizontal = () => (
The perfect latte
Caffè latte is a coffee beverage of Italian origin made with espresso
and steamed milk.
HotCaffeine
)
```
### With Avatar
Use the Card component to display an avatar.
```tsx
'use client'
import { HStack, Stack, Strong, Text, Button, Card } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
import { LuCheck, LuX } from 'react-icons/lu'
export const CardWithAvatar = () => {
return (
Nate Foss
@natefoss
Nate Foss
has requested to join your team. You can approve or decline their
request.
)
}
```
## Props
### Root
# Clipboard
```tsx
'use client'
import { Clipboard } from 'compositions/ui/clipboard'
export const ClipboardBasic = () => {
return (
)
}
```
## Anatomy
```jsx
import { Clipboard } from '#components/ui/clipboard'
```
```jsx
```
## Examples
### Copy Button
Use the `Clipboard.Button` or `Clipboard.IconButton` components to create a copy
button.
```tsx
'use client'
import { Clipboard } from 'compositions/ui/clipboard'
export const ClipboardWithButton = () => {
return (
)
}
```
### Custom labels
Use the `copied` prop to change the label when the text is copied.
```tsx
'use client'
import { Clipboard } from 'compositions/ui/clipboard'
export const ClipboardWithCustomLabels = () => {
return (
Kopiëer
)
}
```
### Input
Use the `Clipboard.Input` component to create a copy input.
```tsx
'use client';
import { Clipboard } from 'compositions/ui/clipboard'
import { InputGroup } from '@chakra-ui/react'
export const ClipboardWithInput = () => {
return (
}
>
)
}
```
### Timeout
Use the `timeout` prop to change the duration of the copy message.
```tsx
'use client'
import { Clipboard } from 'compositions/ui/clipboard'
export const ClipboardWithTimeout = () => {
return (
)
}
```
## Props
### Root
# Code Block
```tsx
'use client'
import { CodeBlock, createShikiAdapter } from '@chakra-ui/react'
import type { HighlighterGeneric } from 'shiki'
const file = {
code: `
Hello, world!
`,
language: 'html',
title: 'index.html',
}
export const CodeBlockBasic = () => {
return (
)
}
const shikiAdapter = createShikiAdapter>({
async load() {
const { createHighlighter } = await import('shiki')
return createHighlighter({
langs: ['html'],
themes: ['github-dark'],
})
},
theme: 'github-dark',
})
```
## Anatomy
```jsx
import { CodeBlock } from '@chakra-ui/react'
```
```jsx
```
## Adapters
Syntax highlighting is provided by an adapter. Create one, pass it to
`CodeBlock.AdapterProvider` at the top level of your app, and render
`CodeBlock.Root` within it. Without an adapter the code block falls back to
plain text.
### Shiki
Install the `shiki` package.
```bash
npm install shiki
```
Then create the adapter, which dynamically loads the highlighter for the
languages you need.
```tsx
import { CodeBlock, createShikiAdapter } from '@chakra-ui/react'
import type { HighlighterGeneric } from 'shiki'
const shikiAdapter = createShikiAdapter>({
async load() {
const { createHighlighter } = await import('shiki')
return createHighlighter({
langs: ['tsx', 'html', 'bash', 'json'],
themes: ['github-dark'],
})
},
theme: 'github-dark',
})
const App = () => (
{/* ... */}
)
```
The examples on this page use the `github-dark` theme. Pass a `{ light, dark }`
theme map together with `meta.colorScheme` to follow the active color mode, see
the [themes](#themes) example.
Once an adapter is provided, the `meta` prop on `CodeBlock.Root` unlocks
highlighting features such as `showLineNumbers`, `highlightLines`,
`focusedLineNumbers`, `addedLineNumbers` and `removedLineNumbers`.
### Highlight.js
[Highlight.js](https://highlightjs.org/) is supported as well through
`createHighlightJsAdapter`. Install the `highlight.js` package and register the
languages you need.
```tsx
import { createHighlightJsAdapter } from '@chakra-ui/react'
import hljs from 'highlight.js/lib/core'
const highlightJsAdapter = createHighlightJsAdapter({
async load() {
const languages = {
tsx: () => import('highlight.js/lib/languages/typescript'),
html: () => import('highlight.js/lib/languages/xml'),
}
await Promise.all(
Object.entries(languages).map(async ([language, file]) => {
const { default: langModule } = await file()
hljs.registerLanguage(language, langModule)
}),
)
return hljs
},
})
```
## Examples
### Sizes
Use the `size` prop to change the size of the code block component.
```tsx
'use client'
import { CodeBlock, For, Stack, createShikiAdapter } from '@chakra-ui/react'
import type { HighlighterGeneric } from 'shiki'
const file = {
code: `
Hello, world!
`,
language: 'html',
title: 'index.html',
}
export const CodeBlockWithSizes = () => {
return (
{(size) => (
(size={size})
)}
)
}
const shikiAdapter = createShikiAdapter>({
async load() {
const { createHighlighter } = await import('shiki')
return createHighlighter({
langs: ['html'],
themes: ['github-dark'],
})
},
theme: 'github-dark',
})
```
### Title
Render the `CodeBlock.Title` component within the `CodeBlock.Header` component
to add a title to the code block component.
```tsx
'use client'
import { CodeBlock, Icon, createShikiAdapter } from '@chakra-ui/react'
import { FaHtml5 } from 'react-icons/fa'
import type { HighlighterGeneric } from 'shiki'
const file = {
code: `
)
}
export const items = [
{ value: "AD", label: "Andorra", emoji: "🇦🇩" },
{ value: "AE", label: "United Arab Emirates", emoji: "🇦🇪" },
{ value: "AF", label: "Afghanistan", emoji: "🇦🇫" },
{ value: "AG", label: "Antigua and Barbuda", emoji: "🇦🇬" },
{ value: "AI", label: "Anguilla", emoji: "🇦🇮" },
{ value: "AL", label: "Albania", emoji: "🇦🇱" },
{ value: "AM", label: "Armenia", emoji: "🇦🇲" },
{ value: "AO", label: "Angola", emoji: "🇦🇴" },
{ value: "AQ", label: "Antarctica", emoji: "🇦🇶" },
{ value: "AR", label: "Argentina", emoji: "🇦🇷" },
{ value: "AS", label: "American Samoa", emoji: "🇦🇸" },
{ value: "AT", label: "Austria", emoji: "🇦🇹" },
{ value: "AU", label: "Australia", emoji: "🇦🇺" },
{ value: "AW", label: "Aruba", emoji: "🇦🇼" },
{ value: "AX", label: "Åland Islands", emoji: "🇦🇽" },
{ value: "AZ", label: "Azerbaijan", emoji: "🇦🇿" },
{ value: "BA", label: "Bosnia and Herzegovina", emoji: "🇧🇦" },
{ value: "BB", label: "Barbados", emoji: "🇧🇧" },
{ value: "BD", label: "Bangladesh", emoji: "🇧🇩" },
{ value: "BE", label: "Belgium", emoji: "🇧🇪" },
{ value: "BF", label: "Burkina Faso", emoji: "🇧🇫" },
{ value: "BG", label: "Bulgaria", emoji: "🇧🇬" },
{ value: "BH", label: "Bahrain", emoji: "🇧🇭" },
{ value: "BI", label: "Burundi", emoji: "🇧🇮" },
{ value: "BJ", label: "Benin", emoji: "🇧🇯" },
{ value: "BL", label: "Saint Barthélemy", emoji: "🇧🇱" },
{ value: "BM", label: "Bermuda", emoji: "🇧🇲" },
{ value: "BN", label: "Brunei Darussalam", emoji: "🇧🇳" },
{ value: "BO", label: "Bolivia, Plurinational State of", emoji: "🇧🇴" },
{ value: "BQ", label: "Bonaire, Sint Eustatius and Saba", emoji: "🇧🇶" },
{ value: "BR", label: "Brazil", emoji: "🇧🇷" },
{ value: "BS", label: "Bahamas", emoji: "🇧🇸" },
{ value: "BT", label: "Bhutan", emoji: "🇧🇹" },
{ value: "BV", label: "Bouvet Island", emoji: "🇧🇻" },
{ value: "BW", label: "Botswana", emoji: "🇧🇼" },
{ value: "BY", label: "Belarus", emoji: "🇧🇾" },
{ value: "BZ", label: "Belize", emoji: "🇧🇿" },
{ value: "CA", label: "Canada", emoji: "🇨🇦" },
{ value: "CC", label: "Cocos (Keeling) Islands", emoji: "🇨🇨" },
{ value: "CD", label: "Congo, Democratic Republic of the", emoji: "🇨🇩" },
{ value: "CF", label: "Central African Republic", emoji: "🇨🇫" },
{ value: "CG", label: "Congo", emoji: "🇨🇬" },
{ value: "CH", label: "Switzerland", emoji: "🇨🇭" },
{ value: "CI", label: "Côte d'Ivoire", emoji: "🇨🇮" },
{ value: "CK", label: "Cook Islands", emoji: "🇨🇰" },
{ value: "CL", label: "Chile", emoji: "🇨🇱" },
{ value: "CM", label: "Cameroon", emoji: "🇨🇲" },
{ value: "CN", label: "China", emoji: "🇨🇳" },
{ value: "CO", label: "Colombia", emoji: "🇨🇴" },
{ value: "CR", label: "Costa Rica", emoji: "🇨🇷" },
{ value: "CU", label: "Cuba", emoji: "🇨🇺" },
{ value: "CV", label: "Cabo Verde", emoji: "🇨🇻" },
{ value: "CW", label: "Curaçao", emoji: "🇨🇼" },
{ value: "CX", label: "Christmas Island", emoji: "🇨🇽" },
{ value: "CY", label: "Cyprus", emoji: "🇨🇾" },
{ value: "CZ", label: "Czechia", emoji: "🇨🇿" },
{ value: "DE", label: "Germany", emoji: "🇩🇪" },
{ value: "DJ", label: "Djibouti", emoji: "🇩🇯" },
{ value: "DK", label: "Denmark", emoji: "🇩🇰" },
{ value: "DM", label: "Dominica", emoji: "🇩🇲" },
{ value: "DO", label: "Dominican Republic", emoji: "🇩🇴" },
{ value: "DZ", label: "Algeria", emoji: "🇩🇿" },
{ value: "EC", label: "Ecuador", emoji: "🇪🇨" },
{ value: "EE", label: "Estonia", emoji: "🇪🇪" },
{ value: "EG", label: "Egypt", emoji: "🇪🇬" },
{ value: "EH", label: "Western Sahara", emoji: "🇪🇭" },
{ value: "ER", label: "Eritrea", emoji: "🇪🇷" },
{ value: "ES", label: "Spain", emoji: "🇪🇸" },
{ value: "ET", label: "Ethiopia", emoji: "🇪🇹" },
{ value: "FI", label: "Finland", emoji: "🇫🇮" },
{ value: "FJ", label: "Fiji", emoji: "🇫🇯" },
{ value: "FK", label: "Falkland Islands (Malvinas)", emoji: "🇫🇰" },
{ value: "FM", label: "Micronesia, Federated States of", emoji: "🇫🇲" },
{ value: "FO", label: "Faroe Islands", emoji: "🇫🇴" },
{ value: "FR", label: "France", emoji: "🇫🇷" },
{ value: "GA", label: "Gabon", emoji: "🇬🇦" },
{
value: "GB",
label: "United Kingdom of Great Britain and Northern Ireland",
emoji: "🇬🇧",
},
{ value: "GD", label: "Grenada", emoji: "🇬🇩" },
{ value: "GE", label: "Georgia", emoji: "🇬🇪" },
{ value: "GF", label: "French Guiana", emoji: "🇬🇫" },
{ value: "GG", label: "Guernsey", emoji: "🇬🇬" },
{ value: "GH", label: "Ghana", emoji: "🇬🇭" },
{ value: "GI", label: "Gibraltar", emoji: "🇬🇮" },
{ value: "GL", label: "Greenland", emoji: "🇬🇱" },
{ value: "GM", label: "Gambia", emoji: "🇬🇲" },
{ value: "GN", label: "Guinea", emoji: "🇬🇳" },
{ value: "GP", label: "Guadeloupe", emoji: "🇬🇵" },
{ value: "GQ", label: "Equatorial Guinea", emoji: "🇬🇶" },
{ value: "GR", label: "Greece", emoji: "🇬🇷" },
{
value: "GS",
label: "South Georgia and the South Sandwich Islands",
emoji: "🇬🇸",
},
{ value: "GT", label: "Guatemala", emoji: "🇬🇹" },
{ value: "GU", label: "Guam", emoji: "🇬🇺" },
{ value: "GW", label: "Guinea-Bissau", emoji: "🇬🇼" },
{ value: "GY", label: "Guyana", emoji: "🇬🇾" },
{ value: "HK", label: "Hong Kong", emoji: "🇭🇰" },
{ value: "HM", label: "Heard Island and McDonald Islands", emoji: "🇭🇲" },
{ value: "HN", label: "Honduras", emoji: "🇭🇳" },
{ value: "HR", label: "Croatia", emoji: "🇭🇷" },
{ value: "HT", label: "Haiti", emoji: "🇭🇹" },
{ value: "HU", label: "Hungary", emoji: "🇭🇺" },
{ value: "ID", label: "Indonesia", emoji: "🇮🇩" },
{ value: "IE", label: "Ireland", emoji: "🇮🇪" },
{ value: "IL", label: "Israel", emoji: "🇮🇱" },
{ value: "IM", label: "Isle of Man", emoji: "🇮🇲" },
{ value: "IN", label: "India", emoji: "🇮🇳" },
{ value: "IO", label: "British Indian Ocean Territory", emoji: "🇮🇴" },
{ value: "IQ", label: "Iraq", emoji: "🇮🇶" },
{ value: "IR", label: "Iran, Islamic Republic of", emoji: "🇮🇷" },
{ value: "IS", label: "Iceland", emoji: "🇮🇸" },
{ value: "IT", label: "Italy", emoji: "🇮🇹" },
{ value: "JE", label: "Jersey", emoji: "🇯🇪" },
{ value: "JM", label: "Jamaica", emoji: "🇯🇲" },
{ value: "JO", label: "Jordan", emoji: "🇯🇴" },
{ value: "JP", label: "Japan", emoji: "🇯🇵" },
{ value: "KE", label: "Kenya", emoji: "🇰🇪" },
{ value: "KG", label: "Kyrgyzstan", emoji: "🇰🇬" },
{ value: "KH", label: "Cambodia", emoji: "🇰🇭" },
{ value: "KI", label: "Kiribati", emoji: "🇰🇮" },
{ value: "KM", label: "Comoros", emoji: "🇰🇲" },
{ value: "KN", label: "Saint Kitts and Nevis", emoji: "🇰🇳" },
{ value: "KP", label: "Korea, Democratic People's Republic of", emoji: "🇰🇵" },
{ value: "KR", label: "Korea, Republic of", emoji: "🇰🇷" },
{ value: "KW", label: "Kuwait", emoji: "🇰🇼" },
{ value: "KY", label: "Cayman Islands", emoji: "🇰🇾" },
{ value: "KZ", label: "Kazakhstan", emoji: "🇰🇿" },
{ value: "LA", label: "Lao People's Democratic Republic", emoji: "🇱🇦" },
{ value: "LB", label: "Lebanon", emoji: "🇱🇧" },
{ value: "LC", label: "Saint Lucia", emoji: "🇱🇨" },
{ value: "LI", label: "Liechtenstein", emoji: "🇱🇮" },
{ value: "LK", label: "Sri Lanka", emoji: "🇱🇰" },
{ value: "LR", label: "Liberia", emoji: "🇱🇷" },
{ value: "LS", label: "Lesotho", emoji: "🇱🇸" },
{ value: "LT", label: "Lithuania", emoji: "🇱🇹" },
{ value: "LU", label: "Luxembourg", emoji: "🇱🇺" },
{ value: "LV", label: "Latvia", emoji: "🇱🇻" },
{ value: "LY", label: "Libya", emoji: "🇱🇾" },
{ value: "MA", label: "Morocco", emoji: "🇲🇦" },
{ value: "MC", label: "Monaco", emoji: "🇲🇨" },
{ value: "MD", label: "Moldova, Republic of", emoji: "🇲🇩" },
{ value: "ME", label: "Montenegro", emoji: "🇲🇪" },
{ value: "MF", label: "Saint Martin, (French part)", emoji: "🇲🇫" },
{ value: "MG", label: "Madagascar", emoji: "🇲🇬" },
{ value: "MH", label: "Marshall Islands", emoji: "🇲🇭" },
{ value: "MK", label: "North Macedonia", emoji: "🇲🇰" },
{ value: "ML", label: "Mali", emoji: "🇲🇱" },
{ value: "MM", label: "Myanmar", emoji: "🇲🇲" },
{ value: "MN", label: "Mongolia", emoji: "🇲🇳" },
{ value: "MO", label: "Macao", emoji: "🇲🇴" },
{ value: "MP", label: "Northern Mariana Islands", emoji: "🇲🇵" },
{ value: "MQ", label: "Martinique", emoji: "🇲🇶" },
{ value: "MR", label: "Mauritania", emoji: "🇲🇷" },
{ value: "MS", label: "Montserrat", emoji: "🇲🇸" },
{ value: "MT", label: "Malta", emoji: "🇲🇹" },
{ value: "MU", label: "Mauritius", emoji: "🇲🇺" },
{ value: "MV", label: "Maldives", emoji: "🇲🇻" },
{ value: "MW", label: "Malawi", emoji: "🇲🇼" },
{ value: "MX", label: "Mexico", emoji: "🇲🇽" },
{ value: "MY", label: "Malaysia", emoji: "🇲🇾" },
{ value: "MZ", label: "Mozambique", emoji: "🇲🇿" },
{ value: "NA", label: "Namibia", emoji: "🇳🇦" },
{ value: "NC", label: "New Caledonia", emoji: "🇳🇨" },
{ value: "NE", label: "Niger", emoji: "🇳🇪" },
{ value: "NF", label: "Norfolk Island", emoji: "🇳🇫" },
{ value: "NG", label: "Nigeria", emoji: "🇳🇬" },
{ value: "NI", label: "Nicaragua", emoji: "🇳🇮" },
{ value: "NL", label: "Netherlands", emoji: "🇳🇱" },
{ value: "NO", label: "Norway", emoji: "🇳🇴" },
{ value: "NP", label: "Nepal", emoji: "🇳🇵" },
{ value: "NR", label: "Nauru", emoji: "🇳🇷" },
{ value: "NU", label: "Niue", emoji: "🇳🇺" },
{ value: "NZ", label: "New Zealand", emoji: "🇳🇿" },
{ value: "OM", label: "Oman", emoji: "🇴🇲" },
{ value: "PA", label: "Panama", emoji: "🇵🇦" },
{ value: "PE", label: "Peru", emoji: "🇵🇪" },
{ value: "PF", label: "French Polynesia", emoji: "🇵🇫" },
{ value: "PG", label: "Papua New Guinea", emoji: "🇵🇬" },
{ value: "PH", label: "Philippines", emoji: "🇵🇭" },
{ value: "PK", label: "Pakistan", emoji: "🇵🇰" },
{ value: "PL", label: "Poland", emoji: "🇵🇱" },
{ value: "PM", label: "Saint Pierre and Miquelon", emoji: "🇵🇲" },
{ value: "PN", label: "Pitcairn", emoji: "🇵🇳" },
{ value: "PR", label: "Puerto Rico", emoji: "🇵🇷" },
{ value: "PS", label: "Palestine, State of", emoji: "🇵🇸" },
{ value: "PT", label: "Portugal", emoji: "🇵🇹" },
{ value: "PW", label: "Palau", emoji: "🇵🇼" },
{ value: "PY", label: "Paraguay", emoji: "🇵🇾" },
{ value: "QA", label: "Qatar", emoji: "🇶🇦" },
{ value: "RE", label: "Réunion", emoji: "🇷🇪" },
{ value: "RO", label: "Romania", emoji: "🇷🇴" },
{ value: "RS", label: "Serbia", emoji: "🇷🇸" },
{ value: "RU", label: "Russian Federation", emoji: "🇷🇺" },
{ value: "RW", label: "Rwanda", emoji: "🇷🇼" },
{ value: "SA", label: "Saudi Arabia", emoji: "🇸🇦" },
{ value: "SB", label: "Solomon Islands", emoji: "🇸🇧" },
{ value: "SC", label: "Seychelles", emoji: "🇸🇨" },
{ value: "SD", label: "Sudan", emoji: "🇸🇩" },
{ value: "SE", label: "Sweden", emoji: "🇸🇪" },
{ value: "SG", label: "Singapore", emoji: "🇸🇬" },
{
value: "SH",
label: "Saint Helena, Ascension and Tristan da Cunha",
emoji: "🇸🇭",
},
{ value: "SI", label: "Slovenia", emoji: "🇸🇮" },
{ value: "SJ", label: "Svalbard and Jan Mayen", emoji: "🇸🇯" },
{ value: "SK", label: "Slovakia", emoji: "🇸🇰" },
{ value: "SL", label: "Sierra Leone", emoji: "🇸🇱" },
{ value: "SM", label: "San Marino", emoji: "🇸🇲" },
{ value: "SN", label: "Senegal", emoji: "🇸🇳" },
{ value: "SO", label: "Somalia", emoji: "🇸🇴" },
{ value: "SR", label: "Suriname", emoji: "🇸🇷" },
{ value: "SS", label: "South Sudan", emoji: "🇸🇸" },
{ value: "ST", label: "Sao Tome and Principe", emoji: "🇸🇹" },
{ value: "SV", label: "El Salvador", emoji: "🇸🇻" },
{ value: "SX", label: "Sint Maarten, (Dutch part)", emoji: "🇸🇽" },
{ value: "SY", label: "Syrian Arab Republic", emoji: "🇸🇾" },
{ value: "SZ", label: "Eswatini", emoji: "🇸🇿" },
{ value: "TC", label: "Turks and Caicos Islands", emoji: "🇹🇨" },
{ value: "TD", label: "Chad", emoji: "🇹🇩" },
{ value: "TF", label: "French Southern Territories", emoji: "🇹🇫" },
{ value: "TG", label: "Togo", emoji: "🇹🇬" },
{ value: "TH", label: "Thailand", emoji: "🇹🇭" },
{ value: "TJ", label: "Tajikistan", emoji: "🇹🇯" },
{ value: "TK", label: "Tokelau", emoji: "🇹🇰" },
{ value: "TL", label: "Timor-Leste", emoji: "🇹🇱" },
{ value: "TM", label: "Turkmenistan", emoji: "🇹🇲" },
{ value: "TN", label: "Tunisia", emoji: "🇹🇳" },
{ value: "TO", label: "Tonga", emoji: "🇹🇴" },
{ value: "TR", label: "Türkiye", emoji: "🇹🇷" },
{ value: "TT", label: "Trinidad and Tobago", emoji: "🇹🇹" },
{ value: "TV", label: "Tuvalu", emoji: "🇹🇻" },
{ value: "TW", label: "Taiwan, Province of China", emoji: "🇹🇼" },
{ value: "TZ", label: "Tanzania, United Republic of", emoji: "🇹🇿" },
{ value: "UA", label: "Ukraine", emoji: "🇺🇦" },
{ value: "UG", label: "Uganda", emoji: "🇺🇬" },
{ value: "UM", label: "United States Minor Outlying Islands", emoji: "🇺🇲" },
{ value: "US", label: "United States of America", emoji: "🇺🇸" },
{ value: "UY", label: "Uruguay", emoji: "🇺🇾" },
{ value: "UZ", label: "Uzbekistan", emoji: "🇺🇿" },
{ value: "VA", label: "Holy See", emoji: "🇻🇦" },
{ value: "VC", label: "Saint Vincent and the Grenadines", emoji: "🇻🇨" },
{ value: "VE", label: "Venezuela, Bolivarian Republic of", emoji: "🇻🇪" },
{ value: "VG", label: "Virgin Islands, British", emoji: "🇻🇬" },
{ value: "VI", label: "Virgin Islands, U.S.", emoji: "🇻🇮" },
{ value: "VN", label: "Viet Nam", emoji: "🇻🇳" },
{ value: "VU", label: "Vanuatu", emoji: "🇻🇺" },
{ value: "WF", label: "Wallis and Futuna", emoji: "🇼🇫" },
{ value: "WS", label: "Samoa", emoji: "🇼🇸" },
{ value: "YE", label: "Yemen", emoji: "🇾🇪" },
{ value: "YT", label: "Mayotte", emoji: "🇾🇹" },
{ value: "ZA", label: "South Africa", emoji: "🇿🇦" },
{ value: "ZM", label: "Zambia", emoji: "🇿🇲" },
{ value: "ZW", label: "Zimbabwe", emoji: "🇿🇼" },
]
```
### Links
Use the `asChild` prop to render the combobox items as links.
```tsx
"use client"
import {
Combobox,
Portal,
useFilter,
useListCollection,
} from "@chakra-ui/react"
import { LuExternalLink } from "react-icons/lu"
export const ComboboxWithLinks = () => {
const { contains } = useFilter({ sensitivity: "base" })
const { collection, filter } = useListCollection({
initialItems: frameworks,
filter: contains,
})
return (
filter(e.inputValue)}
width="320px"
selectionBehavior="clear"
>
Select frameworkNo items found
{collection.items.map((item) => (
{item.label}
))}
)
}
const frameworks = [
{ label: "React", value: "react", docs: "https://react.dev" },
{ label: "Solid", value: "solid", docs: "https://solidjs.com" },
{ label: "Vue", value: "vue", docs: "https://vuejs.org" },
{ label: "Angular", value: "angular", docs: "https://angular.io" },
{ label: "Svelte", value: "svelte", docs: "https://svelte.dev" },
{ label: "Preact", value: "preact", docs: "https://preactjs.com" },
{ label: "Qwik", value: "qwik", docs: "https://qwik.builder.io" },
{ label: "Lit", value: "lit", docs: "https://lit.dev" },
{ label: "Alpine.js", value: "alpinejs", docs: "https://alpinejs.dev" },
{ label: "Ember", value: "ember", docs: "https://emberjs.com" },
{ label: "Next.js", value: "nextjs", docs: "https://nextjs.org" },
]
```
For custom router links, you can customize the `navigate` prop on the
`Combobox.Root` component.
Here's an example of using the Tanstack Router.
```tsx {8-10}
import { Combobox } from '@chakra-ui/react'
import { useNavigate } from '@tanstack/react-router'
function Demo() {
const navigate = useNavigate()
return (
{
navigate({ to: href })
}}
>
{/* ... */}
)
}
```
### Rehydrate Value
In some cases, where a combobox has a `defaultValue` but the collection is not
loaded yet, here's an example of how to rehydrate the value and populate the
input value.
```tsx
"use client"
import {
Combobox,
HStack,
Portal,
Span,
Spinner,
useCombobox,
useListCollection,
} from "@chakra-ui/react"
import { useRef, useState } from "react"
import { useAsync } from "react-use"
export const ComboboxRehydrateValue = () => {
const [inputValue, setInputValue] = useState("")
const { collection, set } = useListCollection({
initialItems: [],
itemToString: (item) => item.name,
itemToValue: (item) => item.name,
})
const combobox = useCombobox({
collection,
defaultValue: ["C-3PO"],
placeholder: "Example: Dexter",
inputValue,
onInputValueChange: (e) => setInputValue(e.inputValue),
})
const state = useAsync(async () => {
const response = await fetch(
`https://swapi.py4e.com/api/people/?search=${inputValue}`,
)
const data = await response.json()
set(data.results)
}, [inputValue, set])
// Rehydrate the value
const hydrated = useRef(false)
if (combobox.value.length && collection.size && !hydrated.current) {
const inputValue = collection.stringify(combobox.value[0])
combobox.setInputValue(inputValue || "")
hydrated.current = true
}
return (
Search Star Wars Characters
{state.loading ? (
Loading...
) : state.error ? (
{state.error.message}
) : (
collection.items.map((item) => (
{item.name}
{item.height}cm / {item.mass}kg
))
)}
)
}
interface Character {
name: string
height: string
mass: string
created: string
edited: string
url: string
}
```
### Custom Item
Customize the appearance of items in the dropdown with your own components.
```tsx
"use client"
import {
Combobox,
HStack,
Image,
Portal,
Span,
Stack,
useComboboxContext,
useFilter,
useListCollection,
} from "@chakra-ui/react"
function ComboboxValue() {
const combobox = useComboboxContext()
const selectedItems = combobox.selectedItems as (typeof items)[number][]
return (
{selectedItems.map((item) => (
{item.label}
))}
)
}
export const ComboboxWithCustomItem = () => {
const { contains } = useFilter({ sensitivity: "base" })
const { collection, filter } = useListCollection({
initialItems: items,
filter: contains,
})
return (
filter(e.inputValue)}
width="320px"
placeholder="Example: Audi"
multiple
closeOnSelect
>
Search and select car brandsNo items found
{collection.items.map((item) => (
{item.label}
))}
)
}
export const items = [
{
label: "Audi",
value: "audi",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/audi-logo.png",
},
{
label: "BMW",
value: "bmw",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/bmw-logo.png",
},
{
label: "Citroen",
value: "citroen",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/citroen-logo.png",
},
{
label: "Dacia",
value: "dacia",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/dacia-logo.png",
},
{
label: "Fiat",
value: "fiat",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/fiat-logo.png",
},
{
label: "Ford",
value: "ford",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/ford-logo.png",
},
{
label: "Ferrari",
value: "ferrari",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/ferrari-logo.png",
},
{
label: "Honda",
value: "honda",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/honda-logo.png",
},
{
label: "Hyundai",
value: "hyundai",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/hyundai-logo.png",
},
{
label: "Jaguar",
value: "jaguar",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/jaguar-logo.png",
},
{
label: "Jeep",
value: "jeep",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/jeep-logo.png",
},
{
label: "Kia",
value: "kia",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/kia-logo.png",
},
{
label: "Land Rover",
value: "land rover",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/land-rover-logo.png",
},
{
label: "Mazda",
value: "mazda",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/mazda-logo.png",
},
{
label: "Mercedes",
value: "mercedes",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/mercedes-logo.png",
},
{
label: "Mini",
value: "mini",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/mini-logo.png",
},
{
label: "Mitsubishi",
value: "mitsubishi",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/mitsubishi-logo.png",
},
{
label: "Nissan",
value: "nissan",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/nissan-logo.png",
},
{
label: "Opel",
value: "opel",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/opel-logo.png",
},
{
label: "Peugeot",
value: "peugeot",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/peugeot-logo.png",
},
{
label: "Porsche",
value: "porsche",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/porsche-logo.png",
},
{
label: "Renault",
value: "renault",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/renault-logo.png",
},
{
label: "Saab",
value: "saab",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/saab-logo.png",
},
{
label: "Skoda",
value: "skoda",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/skoda-logo.png",
},
{
label: "Subaru",
value: "subaru",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/subaru-logo.png",
},
{
label: "Suzuki",
value: "suzuki",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/suzuki-logo.png",
},
{
label: "Toyota",
value: "toyota",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/toyota-logo.png",
},
{
label: "Volkswagen",
value: "volkswagen",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/volkswagen-logo.png",
},
{
label: "Volvo",
value: "volvo",
logo: "https://s3.amazonaws.com/cdn.formk.it/example-assets/car-brands/volvo-logo.png",
},
]
```
### Custom Filter
Here's an example of a custom filter that matches multiple properties of an
item.
```tsx
"use client"
import {
Combobox,
Portal,
Span,
Stack,
useListCollection,
} from "@chakra-ui/react"
export const ComboboxWithCustomFilter = () => {
const { collection, set } = useListCollection({
initialItems: people,
itemToString: (item) => item.name,
itemToValue: (item) => item.id.toString(),
})
const handleInputChange = (details: Combobox.InputValueChangeDetails) => {
const filteredItems = people.filter((item) => {
const searchLower = details.inputValue.toLowerCase()
const nameParts = item.name.toLowerCase().split(" ")
const emailParts = item.email.toLowerCase().split("@")[0].split(".")
return (
item.name.toLowerCase().includes(searchLower) ||
nameParts.some((part) => part.includes(searchLower)) ||
emailParts.some((part) => part.includes(searchLower)) ||
item.role.toLowerCase().includes(searchLower)
)
})
set(filteredItems)
}
return (
Select PersonNo matches found
{collection.items.map((person) => (
{person.name}
{person.email}
))}
)
}
const people = [
{
id: 1,
name: "John Smith",
email: "john@example.com",
role: "Sales Manager",
},
{
id: 2,
name: "Sarah Johnson",
email: "sarah@example.com",
role: "UI Designer",
},
{
id: 3,
name: "Michael Brown",
email: "michael@example.com",
role: "Software Engineer",
},
{
id: 4,
name: "Emily Davis",
email: "emily@example.com",
role: "AI Engineer",
},
{
id: 5,
name: "James Wilson",
email: "james@example.com",
role: "Chief Executive Officer",
},
]
```
### Custom Animation
To customize the animation of the combobox, pass the `_open` and `_closed` prop
to the `Combobox.Content` component.
```tsx
"use client"
import {
Combobox,
Portal,
useFilter,
useListCollection,
} from "@chakra-ui/react"
export const ComboboxWithCustomAnimation = () => {
const { contains } = useFilter({ sensitivity: "base" })
const { collection, filter } = useListCollection({
initialItems: frameworks,
filter: contains,
})
return (
filter(e.inputValue)}
width="320px"
positioning={{ flip: false, gutter: 2 }}
>
Select frameworkNo items found
{collection.items.map((item) => (
{item.label}
))}
)
}
const frameworks = [
{ label: "React", value: "react" },
{ label: "Solid", value: "solid" },
{ label: "Vue", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
{ label: "Preact", value: "preact" },
{ label: "Qwik", value: "qwik" },
{ label: "Lit", value: "lit" },
{ label: "Alpine.js", value: "alpinejs" },
{ label: "Ember", value: "ember" },
{ label: "Next.js", value: "nextjs" },
]
```
### Popover
To use the combobox within a popover component, avoid wrapping the
`Combobox.Positioner` within the `Portal`.
```diff
-
{/* ... */}
-
```
```tsx
"use client"
import {
Button,
Combobox,
Popover,
Portal,
useFilter,
useListCollection,
} from "@chakra-ui/react"
export const ComboboxInPopover = () => {
return (
Select framework
)
}
const ComboboxDemo = () => {
const { contains } = useFilter({ sensitivity: "base" })
const { collection, filter } = useListCollection({
initialItems: frameworks,
filter: contains,
})
return (
filter(e.inputValue)}
>
No items found
{collection.items.map((item) => (
{item.label}
))}
)
}
const frameworks = [
{ label: "React", value: "react" },
{ label: "Solid", value: "solid" },
{ label: "Vue", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
{ label: "Preact", value: "preact" },
{ label: "Qwik", value: "qwik" },
{ label: "Lit", value: "lit" },
{ label: "Alpine.js", value: "alpinejs" },
{ label: "Ember", value: "ember" },
{ label: "Next.js", value: "nextjs" },
]
```
## Props
### Root
### Item
# Date Picker
```tsx
"use client"
import { DatePicker, Portal } from "@chakra-ui/react"
import { LuCalendar } from "react-icons/lu"
export const DatePickerBasic = () => {
return (
Date of birth
)
}
```
## Anatomy
```jsx
import { DatePicker } from '@chakra-ui/react'
```
```jsx
```
Date values are represented by objects from
[`@internationalized/date`](https://react-aria.adobe.com/internationalized/date/),
which handle timezone-safe, locale-aware date math. The `parseDate` helper and
the `DateValue` type are re-exported from `@chakra-ui/react`, so you don't need
to install the package yourself.
```tsx
import { parseDate } from '@chakra-ui/react'
import type { DateValue } from '@chakra-ui/react'
```
## Examples
### Basic
Compose `DatePicker.Input` with a `DatePicker.Trigger` to render a text input
that opens a calendar popover.
```tsx
"use client"
import { DatePicker, Portal } from "@chakra-ui/react"
import { LuCalendar } from "react-icons/lu"
export const DatePickerBasic = () => {
return (
Date of birth
)
}
```
### Sizes
Use the `size` prop to change the size of the date picker.
```tsx
"use client"
import { DatePicker, For, Portal, Stack } from "@chakra-ui/react"
import { LuCalendar } from "react-icons/lu"
export const DatePickerWithSizes = () => {
return (
{(size) => (
Select date - {size}
)}
)
}
```
### Variants
Use the `variant` prop to change the visual style of the date picker.
```tsx
"use client"
import { DatePicker, For, Portal, Stack } from "@chakra-ui/react"
import { LuCalendar } from "react-icons/lu"
export const DatePickerWithVariants = () => {
return (
{(variant) => (
Select date - {variant}
)}
)
}
```
### Controlled
Use the `value` and `onValueChange` props to control the selected date. The
value is always an array of `DateValue` objects, even in single selection mode.
```tsx
"use client"
import type { DateValue } from "@chakra-ui/react"
import { DatePicker, Portal, Stack, Text, parseDate } from "@chakra-ui/react"
import { useState } from "react"
import { LuCalendar } from "react-icons/lu"
export const DatePickerControlled = () => {
const [value, setValue] = useState([parseDate("2026-01-26")])
return (
Selected: {value.map((d) => d.toString()).join(", ")}
setValue(e.value)}>
Date of birth
)
}
```
### Range Selection
Set the `selectionMode` prop to `"range"` to allow selecting a start and end
date. Render two `DatePicker.Input` components and pass the `index` prop to bind
each one to a side of the range.
```tsx
"use client"
import { DatePicker, Portal } from "@chakra-ui/react"
import { LuCalendar } from "react-icons/lu"
export const DatePickerRangeSelection = () => {
return (
Select range
)
}
```
### Min and Max
Use the `min` and `max` props to restrict date selection to a specific range.
```tsx
"use client"
import { DatePicker, Portal, parseDate } from "@chakra-ui/react"
import { LuCalendar } from "react-icons/lu"
export const DatePickerMinMax = () => {
return (
Date of creation
)
}
```
### Clear Trigger
Use `DatePicker.Context` to swap the trigger for a `DatePicker.ClearTrigger`
once a date has been selected.
```tsx
"use client"
import { DatePicker, Portal } from "@chakra-ui/react"
import { LuCalendar } from "react-icons/lu"
export const DatePickerWithClear = () => {
return (
Date of birth
{(context) =>
context.value.length ? (
) : (
)
}
)
}
```
### Presets
Render the `DatePicker.PresetTrigger` component to provide quick date selection
options like "Last 7 days" or "This month" next to the calendar.
```tsx
"use client"
import { Button, DatePicker, Flex, Portal, VStack } from "@chakra-ui/react"
import { LuCalendar } from "react-icons/lu"
export const DatePickerPresets = () => {
return (
Select range
)
}
```
### Calendar
Set the `inline` prop on `DatePicker.Root` to render the calendar directly on
the page, without the input, positioner and popup content. Use this when the
calendar is the main interface, like a booking or scheduling view.
```tsx
"use client"
import { DatePicker } from "@chakra-ui/react"
export const DatePickerCalendarBasic = () => {
return (
)
}
```
Inline calendars support every selection mode. Set `selectionMode` to `"range"`
to let people pick a start and end date without opening a popup.
```tsx
"use client"
import { DatePicker } from "@chakra-ui/react"
export const DatePickerCalendarRangeSelection = () => {
return (
)
}
```
### Date Input
Render only `DatePicker.Control` with a `DatePicker.Input` to get a date field
without a calendar popup. Use this when people already know the date they want
and typing it is faster than picking it, for example a date of birth.
```tsx
"use client"
import { DatePicker } from "@chakra-ui/react"
export const DatePickerInputOnly = () => {
return (
Date of birth
)
}
```
## Props
### Root
### Input
### View
### TableCell
### PresetTrigger
# Editable
```tsx
'use client'
import { Editable } from '@chakra-ui/react'
export const EditableBasic = () => (
)
```
## Anatomy
```jsx
import { Editable } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Controlled
```tsx
'use client'
import { useState } from 'react'
import { Editable } from '@chakra-ui/react'
export const EditableControlled = () => {
const [name, setName] = useState('')
return (
setName(e.value)}
placeholder="Click to edit"
>
)
}
```
### With Double Click
Use the `activationMode` prop to make the content editable when users double
click.
```tsx
'use client'
import { Editable } from '@chakra-ui/react'
export const EditableWithDoubleClick = () => (
)
```
### Disabled
Use the `disabled` prop to disable the editable component.
```tsx
'use client'
import { Editable } from '@chakra-ui/react'
export const EditableDisabled = () => {
return (
)
}
```
### Invalid
Use the `invalid` prop to indicate that the editable component is invalid.
```tsx
'use client'
import { Editable } from '@chakra-ui/react'
export const EditableInvalid = () => {
return (
)
}
```
### TextArea
You can make a text area editable.
```tsx
'use client'
import { Editable } from '@chakra-ui/react'
export const EditableWithTextarea = () => {
return (
)
}
```
### With Controls
Add controls such as "edit", "cancel" and "submit" to `Editable` for better user
experience.
```tsx
'use client';
import { Editable } from '@chakra-ui/react'
import { IconButton } from 'compositions/ui/icon-button'
import { LuCheck, LuPencilLine, LuX } from 'react-icons/lu'
export const EditableWithControls = () => {
return (
)
}
```
## Props
### Root
# Field
```tsx
'use client'
import { Field, Input } from '@chakra-ui/react'
export const FieldBasic = () => {
return (
Email
)
}
```
## Anatomy
```jsx
import { Field } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Helper Text
Use the `helperText` prop to add helper text to the field.
```tsx
'use client'
import { Field, Input } from '@chakra-ui/react'
export const FieldWithHelperText = () => {
return (
EmailThis is a helper text
)
}
```
### Error Text
Use the `invalid` and `errorText` to indicate that the field is invalid.
```tsx
'use client'
import { Field, Input } from '@chakra-ui/react'
export const FieldWithErrorText = () => {
return (
EmailThis is an error text
)
}
```
### Disabled
Use the `disabled` prop to disable the field.
```tsx
'use client'
import { Field, Input } from '@chakra-ui/react'
export const FieldWithDisabled = () => {
return (
Email
)
}
```
### Textarea
Here's how to use the field component with a textarea.
```tsx
'use client'
import { Field, Textarea } from '@chakra-ui/react'
export const FieldWithTextarea = () => {
return (
Message
)
}
```
### Native Select
Here's how to use the field component with a native select.
```tsx
'use client';
import { Field } from '@chakra-ui/react'
import { NativeSelect } from 'compositions/ui/native-select'
export const FieldWithNativeSelect = () => {
return (
Native Select
)
}
```
## Props
### Root
# Fieldset
```tsx
import { Button, Field, Fieldset, Input, Stack } from '@chakra-ui/react'
import { NativeSelect } from 'compositions/ui/native-select'
export const FieldsetBasic = () => {
return (
Contact details
Please provide your contact details below.
NameEmail addressCountry
)
}
```
## Anatomy
```jsx
import { Fieldset } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Basic
Group related fields under a common `Fieldset.Legend`. Use the `size` prop on
the `Fieldset.Root` to change the spacing and typography of the group.
```tsx
import { Button, Field, Fieldset, Input, Stack } from '@chakra-ui/react'
import { NativeSelect } from 'compositions/ui/native-select'
export const FieldsetBasic = () => {
return (
Contact details
Please provide your contact details below.
NameEmail addressCountry
)
}
```
### Disabled
Use the `disabled` prop to disable the fieldset to disable all input elements
within the fieldset.
```tsx
'use client';
import { Field, Fieldset, Input, Textarea } from '@chakra-ui/react'
import { NativeSelect } from 'compositions/ui/native-select'
export const FieldsetWithDisabled = () => {
return (
Shipping detailsStreet addressCountryDelivery notes
)
}
```
### Invalid
Use the `invalid` prop to mark the fieldset as invalid. This will show the error
text.
> Note: You need to pass the `invalid` prop to the `Field` component within the
> fieldset to make each input element invalid.
```tsx
'use client'
import { Field, Fieldset, Input, Textarea } from '@chakra-ui/react'
import { NativeSelect } from 'compositions/ui/native-select'
export const FieldsetWithInvalid = () => {
return (
Shipping detailsStreet addressCountryNotes
Some fields are invalid. Please check them.
)
}
```
## Props
# File Upload
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { FileUpload } from 'compositions/ui/file-upload'
import { HiUpload } from 'react-icons/hi'
export const FileUploadBasic = () => {
return (
)
}
```
## Anatomy
```jsx
import { FileUpload } from '#components/ui/file-upload'
```
```jsx
```
## Examples
### Directory
Use the `directory` prop to allow selecting a directory instead of a file.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { FileUpload } from 'compositions/ui/file-upload'
import { HiUpload } from 'react-icons/hi'
export const FileUploadDirectory = () => {
return (
)
}
```
### Media Capture
Use the `capture` prop to select and upload files from different environments
and media types.
> **Note:** This is
> [not fully supported](https://caniuse.com/mdn-api_htmlinputelement_capture) in
> all browsers.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { FileUpload } from 'compositions/ui/file-upload'
import { HiCamera } from 'react-icons/hi'
export const FileUploadMediaCapture = () => {
return (
)
}
```
### Multiple Files
Upload multiple files at once by using the `maxFiles` prop.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { FileUpload } from 'compositions/ui/file-upload'
import { HiUpload } from 'react-icons/hi'
export const FileUploadMultiple = () => {
return (
)
}
```
### Dropzone
Drop multiple files inside the dropzone and use the `maxFiles` prop to set the
number of files that can be uploaded at once.
```tsx
'use client'
import { Heading, Text } from '@chakra-ui/react'
import { FileUpload } from 'compositions/ui/file-upload'
export const FileUploadWithDropzone = () => {
return (
Drag and drop here to upload.png, .jpg up to 5MB
)
}
```
### Input
Use the `FileInput` component to create a trigger that looks like a text input.
```tsx
'use client'
import { FileUpload } from 'compositions/ui/file-upload'
export const FileUploadWithInput = () => {
return (
Upload file
)
}
```
### Clearable
Here's an example of a clearable file upload input.
```tsx
'use client';
import { CloseButton } from 'compositions/ui/close-button'
import { FileUpload } from 'compositions/ui/file-upload'
import { InputGroup } from '@chakra-ui/react'
import { LuFileUp } from 'react-icons/lu'
export const FileUploadWithInputClear = () => {
return (
Upload file}
endElement={
}
>
)
}
```
### Accepted Files
Define the accepted files for upload using the `accept` prop.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { FileUpload } from 'compositions/ui/file-upload'
import { HiUpload } from 'react-icons/hi'
export const FileUploadAcceptedFiles = () => {
return (
)
}
```
## Props
### Root
# Input
```tsx
'use client'
import { Input } from '@chakra-ui/react'
export const InputBasic = () => {
return
}
```
## Anatomy
```jsx
import { Input } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Variants
Use the `variant` prop to change the visual style of the input.
```tsx
'use client';
import { Stack, Input } from '@chakra-ui/react'
export const InputWithVariants = () => {
return (
)
}
```
### Sizes
Use the `size` prop to change the size of the input.
```tsx
'use client';
import { Stack, Input } from '@chakra-ui/react'
export const InputWithSizes = () => {
return (
)
}
```
### Helper Text
Pair the input with the `Field` component to add helper text.
```tsx
'use client'
import { Field, Input } from '@chakra-ui/react'
export const InputWithHelperText = () => {
return (
EmailWe'll never share your email.
)
}
```
### Error Text
Pair the input with the `Field` component to add error text.
```tsx
'use client'
import { Field, Input } from '@chakra-ui/react'
export const InputWithErrorText = () => {
return (
EmailThis field is required
)
}
```
### Field
Compose the input with the `Field` component to add a label, helper text, and
error text.
```tsx
'use client';
import { HStack, Field, Input } from '@chakra-ui/react'
export const InputWithField = () => {
return (
Email
)
}
```
### Left and Right Element
Pair the input with the `InputElement` component to add an element to the left
or right of the input.
```tsx
import { HStack, Input, InputGroup } from '@chakra-ui/react'
import { LuUser } from 'react-icons/lu'
export const InputWithLeftElement = () => {
return (
}>
)
}
```
```tsx
import { HStack, Kbd, Input, InputGroup } from '@chakra-ui/react'
import { NativeSelect } from 'compositions/ui/native-select'
import { LuSearch } from 'react-icons/lu'
const DomainSelect = () => (
)
export const InputWithLeftAndRightElement = () => {
return (
}
endElement={⌘K}
>
}
>
)
}
```
### Addon
Use the `InputAddon` and `Group` components to add an addon to the input.
```tsx
'use client';
import { Group, Stack, Input, InputAddon } from '@chakra-ui/react'
export const InputWithAddon = () => (
https://.com
)
```
### Disabled
Use the `disabled` prop to disable the input.
```tsx
'use client'
import { Input } from '@chakra-ui/react'
export const InputWithDisabled = () => {
return
}
```
### Placeholder Style
Use the `_placeholder` prop to style the placeholder text.
```tsx
'use client'
import { Input } from '@chakra-ui/react'
export const InputWithPlaceholderStyle = () => {
return (
)
}
```
## Props
# Listbox
```tsx
"use client"
import { Listbox, createListCollection } from "@chakra-ui/react"
export const ListboxBasic = () => {
return (
Select framework
{frameworks.items.map((framework) => (
{framework.label}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
```
## Anatomy
```jsx
import { Listbox } from '@chakra-ui/react'
```
```jsx
```
To set up the listbox, use `createListCollection` to create a static collection,
or the `useListCollection` hook when the list needs to be filtered or mutated at
runtime.
## Examples
### Basic
Pass a collection to `Listbox.Root` and map over `collection.items` to render
each option.
```tsx
"use client"
import { Listbox, createListCollection } from "@chakra-ui/react"
export const ListboxBasic = () => {
return (
Select framework
{frameworks.items.map((framework) => (
{framework.label}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
```
### Controlled
Control the listbox value externally using the `value` and `onValueChange`
props for custom state management.
```tsx
"use client"
import { Code, Listbox, Stack, createListCollection } from "@chakra-ui/react"
import { useState } from "react"
export const ListboxControlled = () => {
const [value, setValue] = useState([])
return (
setValue(details.value)}
>
Select framework
{frameworks.items.map((framework) => (
{framework.label}
))}
Selected: {JSON.stringify(value, null, 2)}
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
],
})
```
### Multiple Selection
Set the `selectionMode` prop to `"multiple"` to let users select more than one
item, useful for scenarios like choosing tags, categories or preferences.
```tsx
"use client"
import { Listbox, createListCollection } from "@chakra-ui/react"
export const ListboxMultiselect = () => {
return (
Select frameworks (multiple)
{frameworks.items.map((framework) => (
{framework.label}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
{ label: "Next.js", value: "nextjs" },
{ label: "Nuxt.js", value: "nuxtjs" },
],
})
```
### Grouped
Pass a `groupBy` function to the collection and render `Listbox.ItemGroup` to
organize related options under clear section headers.
```tsx
"use client"
import { Listbox, createListCollection } from "@chakra-ui/react"
export const ListboxGrouped = () => {
return (
Select media
{collection.group().map(([category, items]) => (
{category}
{items.map((item) => (
{item.label}
))}
))}
)
}
const collection = createListCollection({
items: [
{ label: "Naruto", value: "naruto", category: "Anime" },
{ label: "One Piece", value: "one-piece", category: "Anime" },
{ label: "Dragon Ball", value: "dragon-ball", category: "Anime" },
{
label: "The Shawshank Redemption",
value: "the-shawshank-redemption",
category: "Movies",
},
{ label: "The Godfather", value: "the-godfather", category: "Movies" },
{ label: "The Dark Knight", value: "the-dark-knight", category: "Movies" },
],
groupBy: (item) => item.category,
})
```
### With Icon
Add icons to listbox items to provide visual context and improve recognition of
different options.
```tsx
"use client"
import { Box, Listbox, createListCollection } from "@chakra-ui/react"
import { LuAtom, LuGlobe, LuPalette, LuZap } from "react-icons/lu"
export const ListboxWithIcon = () => {
return (
Select framework
{frameworks.items.map((framework) => (
{framework.icon}
{framework.label}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: "React.js", value: "react", icon: },
{ label: "Vue.js", value: "vue", icon: },
{ label: "Angular", value: "angular", icon: },
{ label: "Svelte", value: "svelte", icon: },
],
})
```
### With Description
Include additional descriptive text for each item to provide more context and
help users make informed choices.
```tsx
"use client"
import { Box, Listbox, Text, createListCollection } from "@chakra-ui/react"
export const ListboxWithDescription = () => {
return (
Select framework
{frameworks.items.map((framework) => (
{framework.label}
{framework.description}
))}
)
}
const frameworks = createListCollection({
items: [
{
label: "React.js",
value: "react",
description: "A JavaScript library for building user interfaces",
},
{
label: "Vue.js",
value: "vue",
description: "The progressive JavaScript framework",
},
{
label: "Angular",
value: "angular",
description: "Platform for building mobile and desktop web applications",
},
{
label: "Svelte",
value: "svelte",
description: "Cybernetically enhanced web apps",
},
{
label: "Next.js",
value: "nextjs",
description: "The React framework for production",
},
],
})
```
### With Input
Render `Listbox.Input` and filter the collection as the user types to make it
easy to find specific items in long lists. Use `Listbox.Empty` to render a
fallback when nothing matches.
```tsx
"use client"
import { Input, Listbox, useFilter, useListCollection } from "@chakra-ui/react"
export const ListboxWithInput = () => {
const { contains } = useFilter({ sensitivity: "base" })
const { collection, filter } = useListCollection({
initialItems: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
{ label: "Next.js", value: "nextjs" },
{ label: "Nuxt.js", value: "nuxtjs" },
{ label: "Remix", value: "remix" },
{ label: "Gatsby", value: "gatsby" },
{ label: "Ember.js", value: "ember" },
{ label: "Preact", value: "preact" },
],
filter: contains,
})
return (
Select Framework filter(e.target.value)}
/>
{collection.items.map((framework) => (
{framework.label}
))}
No frameworks found
)
}
```
### With Popover
Use the listbox within a popover to create dropdown-like selection menus that
overlay other content without taking up permanent screen space.
```tsx
"use client"
import {
Button,
Listbox,
Popover,
Portal,
useFilter,
useListCollection,
useListbox,
} from "@chakra-ui/react"
import { useRef, useState } from "react"
import { LuChevronDown } from "react-icons/lu"
export const ListboxWithPopover = () => {
const [inputValue, setInputValue] = useState("")
const [open, setOpen] = useState(false)
const { contains } = useFilter({ sensitivity: "base" })
const triggerRef = useRef(null)
const { collection, filter } = useListCollection({
initialItems: [
{ label: "React.js", value: "react" },
{ label: "Vue.js", value: "vue" },
{ label: "Angular", value: "angular" },
{ label: "Svelte", value: "svelte" },
{ label: "Next.js", value: "nextjs" },
{ label: "Nuxt.js", value: "nuxtjs" },
],
filter: contains,
})
const listbox = useListbox({
collection,
onValueChange() {
setOpen(false)
setInputValueFn("")
triggerRef.current?.focus()
},
})
const setInputValueFn = (value: string) => {
setInputValue(value)
filter(value)
}
const selectedItem = listbox.selectedItems[0]
return (
setOpen(e.open)}>
setInputValueFn(e.currentTarget.value)}
/>
{collection.items.map((framework) => (
{framework.label}
))}
)
}
```
## Props
### Root
### Label
### Input
### Content
### Item
### ItemText
### ItemIndicator
### ItemGroup
### ItemGroupLabel
# Select (Native)
```tsx
import { NativeSelect } from 'compositions/ui/native-select'
export const NativeSelectBasic = () => {
return (
)
}
```
## Anatomy
```jsx
import { NativeSelect } from '#components/ui/native-select'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the select component.
```tsx
'use client'
import { For, Stack } from '@chakra-ui/react'
import { NativeSelect } from 'compositions/ui/native-select'
export const NativeSelectWithSizes = () => {
return (
{(size) => (
)}
)
}
```
### Variants
Use the `variant` prop to change the appearance of the select component.
```tsx
'use client'
import { For, Stack } from '@chakra-ui/react'
import { NativeSelect } from 'compositions/ui/native-select'
export const NativeSelectWithVariants = () => {
return (
{(variant) => (
)}
)
}
```
### Items Prop
Pass the `items` prop to the `NativeSelectField` component to render a list of
options.
```tsx
import { NativeSelect } from 'compositions/ui/native-select'
export const NativeSelectWithItems = () => {
return (
)
}
```
### Controlled
Use the `value` and `onChange` props to control the select component.
```tsx
'use client'
import { useState } from 'react'
import { NativeSelect } from 'compositions/ui/native-select'
export const NativeSelectControlled = () => {
const [value, setValue] = useState('')
return (
setValue(e.currentTarget.value)}
>
)
}
```
### Hook Form
Here is an example of how to use the `NativeSelect` component with
`react-hook-form`.
```tsx
'use client';
import { zodResolver } from '@hookform/resolvers/zod'
import { Button, Field } from '@chakra-ui/react'
import { NativeSelect } from 'compositions/ui/native-select'
import { useForm } from 'react-hook-form'
import { z } from 'zod'
const formSchema = z.object({
framework: z.string().min(1, { message: 'Framework is required' }),
})
type FormValues = z.infer
export const NativeSelectWithHookForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(formSchema),
})
const onSubmit = handleSubmit((data) => console.log(data))
return (
)
}
```
## Props
### Root
# Number Input
```tsx
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputBasic = () => {
return
}
```
## Anatomy
```jsx
import { NumberInput } from '#components/ui/number-input'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the number input.
```tsx
import { For, Stack } from '@chakra-ui/react'
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputWithSizes = () => {
return (
{(size) => }
)
}
```
### Formatting
Use the `formatOptions` prop to format the number input value. The value of this
maps to `Intl.NumberFormatOptions` and is applied based on the current locale.
```tsx
import { Stack } from '@chakra-ui/react'
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputWithFormatOptions = () => {
return (
)
}
```
### Min and Max
Use the `min` and `max` props to set the minimum and maximum values of the
number input.
If value entered is less than `min` or greater than `max`, the value will be
clamped to the nearest boundary on blur, or enter key press.
```tsx
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputWithMinMax = () => {
return
}
```
### Step
Use the `step` prop to change the increment or decrement interval of the number
input.
```tsx
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputWithStep = () => {
return
}
```
### Controlled
Use the `value` and `onValueChange` props to control the value of the number
input.
```tsx
'use client'
import { useState } from 'react'
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputControlled = () => {
const [value, setValue] = useState('10')
return (
setValue(e.value)}
/>
)
}
```
### Mouse Wheel
Use the `allowMouseWheel` prop to enable or disable the mouse wheel to change
```tsx
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputWithMouseWheel = () => {
return
}
```
### Disabled
Use the `disabled` prop to disable the number input.
```tsx
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputWithDisabled = () => {
return
}
```
### Invalid
Use the `Field` component and pass the `invalid` prop to indicate that the
number input is invalid.
```tsx
import { Field } from '@chakra-ui/react'
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputWithInvalid = () => {
return (
Enter NumberThe entry is invalid
)
}
```
### Helper Text
Use the `Field` component and pass the `helperText` prop to add helper text to
the number input.
```tsx
import { Field } from '@chakra-ui/react'
import { NumberInput } from 'compositions/ui/number-input'
export const NumberInputWithField = () => {
return (
Enter NumberEnter a number between 1 and 10
)
}
```
### Hook Form
Here is an example of how to use the `NumberInput` component with
`react-hook-form`.
```tsx
'use client'
import { zodResolver } from '@hookform/resolvers/zod'
import { Button, Field } from '@chakra-ui/react'
import { NumberInput } from 'compositions/ui/number-input'
import { Controller, useForm } from 'react-hook-form'
import { z } from 'zod'
const formSchema = z.object({
number: z.string({ message: 'Number is required' }),
})
type FormValues = z.infer
export const NumberInputWithHookForm = () => {
const {
control,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(formSchema),
})
const onSubmit = handleSubmit((data) => console.log(data))
return (
)
}
```
## Props
### Root
# Password Input
```tsx
import { PasswordInput } from 'compositions/ui/password-input'
export const PasswordInputBasic = () => {
return
}
```
## Usage
```jsx
import { PasswordInput } from '#components/ui/password-input'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the password input.
:::info
The password input sizes are mapped to the `Input` component sizes.
:::
```tsx
'use client'
import { Stack } from '@chakra-ui/react'
import { PasswordInput } from 'compositions/ui/password-input'
export const PasswordInputWithSizes = () => {
return (
)
}
```
### Controlled
Use the `value` and `onChange` props to control the current page.
```tsx
'use client'
import { useState } from 'react'
import { PasswordInput } from 'compositions/ui/password-input'
export const PasswordInputControlled = () => {
const [value, setValue] = useState('')
return (
setValue(e.target.value)} />
)
}
```
### Hook Form
Here's an example of how to use the `PasswordInput` component with
`react-hook-form`.
```tsx
'use client';
import { Button, Input, Stack, Field } from '@chakra-ui/react'
import { PasswordInput } from 'compositions/ui/password-input'
import { useForm } from 'react-hook-form'
interface FormValues {
username: string
password: string
}
export const PasswordInputWithHookForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
const onSubmit = handleSubmit((data) => console.log(data))
return (
)
}
```
### Controlled Visibility
Use the `visible` and `onVisibleChange` props to control the visibility of the
password input.
```tsx
'use client'
import { useState } from 'react'
import { Stack, Text } from '@chakra-ui/react'
import { PasswordInput } from 'compositions/ui/password-input'
export const PasswordInputControlledVisibility = () => {
const [visible, setVisible] = useState(false)
return (
Password is {visible ? 'visible' : 'hidden'}
)
}
```
## Props
### Root
# Pin Input
```tsx
'use client'
import { PinInput } from 'compositions/ui/pin-input'
export const PinInputBasic = () => {
return
}
```
## Anatomy
```jsx
import { PinInput } from '#components/ui/pin-input'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the pin input component.
```tsx
'use client'
import { Stack } from '@chakra-ui/react'
import { PinInput } from 'compositions/ui/pin-input'
export const PinInputWithSizes = () => {
return (
)
}
```
### One time code
Use the `otp` prop to make the pin input component behave like a one-time code
input. This helps improve the user experience when entering OTP codes.
```tsx
'use client'
import { PinInput } from 'compositions/ui/pin-input'
export const PinInputWithOtp = () => {
return
}
```
### Mask
Use the `mask` prop to obscure the entered pin code.
```tsx
'use client'
import { PinInput } from 'compositions/ui/pin-input'
export const PinInputWithMask = () => {
return
}
```
### Placeholder
Use the `placeholder` prop to add a placeholder to the pin input component.
```tsx
'use client'
import { PinInput } from 'compositions/ui/pin-input'
export const PinInputWithPlaceholder = () => {
return
}
```
### Field
Here's an example of how to compose the `Field` and the `PinInput` components
```tsx
'use client'
import { Field } from '@chakra-ui/react'
import { PinInput } from 'compositions/ui/pin-input'
export const PinInputWithField = () => {
return (
Enter otp
)
}
```
### Controlled
Use the `value` and `onValueChange` props to control the value of the pin input
```tsx
'use client'
import { useState } from 'react'
import { PinInput } from 'compositions/ui/pin-input'
export const PinInputControlled = () => {
const [value, setValue] = useState(['', '', '', ''])
return setValue(e.value)} />
}
```
### Attached
Use the `attached` prop to attach the pin input to the input field
```tsx
'use client'
import { PinInput } from 'compositions/ui/pin-input'
export const PinInputAttached = () => {
return
}
```
### Alphanumeric
Use the `type` prop to allow the user to enter alphanumeric characters. Values
can be either `alphanumeric`, `numeric`, or `alphabetic`
```tsx
'use client'
import { PinInput } from 'compositions/ui/pin-input'
export const PinInputAlphanumeric = () => {
return
}
```
## Props
### Root
# Radio Card
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { RadioCard } from 'compositions/ui/radio-card'
export const RadioCardBasic = () => {
return (
Select framework
{items.map((item) => (
))}
)
}
const items = [
{ value: 'next', title: 'Next.js', description: 'Best for apps' },
{ value: 'vite', title: 'Vite', description: 'Best for SPAs' },
{ value: 'astro', title: 'Astro', description: 'Best for static sites' },
]
```
## Anatomy
A RadioCard is a form element with a larger interaction surface represented as a
card.
```jsx
import { RadioCard } from '#components/ui/radio-card'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the radio card.
```tsx
'use client'
import { For, HStack, Stack } from '@chakra-ui/react'
import { RadioCard } from 'compositions/ui/radio-card'
export const RadioCardWithSizes = () => {
return (
{(size) => (
size = ({size})
{items.map((item) => (
))}
)}
)
}
const items = [
{ value: 'next', title: 'Next.js' },
{ value: 'vite', title: 'Vite' },
]
```
### Colors
Use the `colorPalette` prop to change the color of the radio card.
```tsx
'use client'
import { For, HStack, Stack } from '@chakra-ui/react'
import { RadioCard } from 'compositions/ui/radio-card'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from '../lib/color-palettes'
export const RadioCardWithColors = () => {
return (
{(colorPalette) => (
Select Framework
{items.map((item) => (
))}
)}
)
}
const items = [
{ value: 'next', title: 'Next.js' },
{ value: 'vite', title: 'Vite' },
]
```
### Icon
Here's an example of a RadioCard with an icon.
```tsx
'use client'
import { HStack, Icon } from '@chakra-ui/react'
import { RadioCard } from 'compositions/ui/radio-card'
import { LuArrowRight, LuCircleOff, LuLock } from 'react-icons/lu'
export const RadioCardWithIcon = () => {
return (
Select permission
{items.map((item) => (
{item.icon}
}
label={item.title}
description={item.description}
key={item.value}
value={item.value}
/>
))}
)
}
const items = [
{
icon: ,
value: 'allow',
title: 'Allow',
description: 'This user can access the system',
},
{
icon: ,
value: 'deny',
title: 'Deny',
description: 'This user will be denied access to the system',
},
{
icon: ,
value: 'lock',
title: 'Lock',
description: 'This user will be locked out of the system',
},
]
```
### Centered
Here's an example of a RadioCard with centered text.
```tsx
'use client'
import { HStack, Icon } from '@chakra-ui/react'
import { RadioCard } from 'compositions/ui/radio-card'
import { LuClock, LuDollarSign, LuTrendingUp } from 'react-icons/lu'
export const RadioCardCentered = () => {
return (
Select contract type
{items.map((item) => (
{item.icon}
}
label={item.title}
key={item.value}
value={item.value}
/>
))}
)
}
const items = [
{ icon: , value: 'fixed', title: 'Fixed Rate' },
{ icon: , value: 'milestone', title: 'Milestone' },
{ icon: , value: 'hourly', title: 'Hourly' },
]
```
### Addon
Use the `RadioCardItemAddon` component to add an addon below the radio card
content.
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { RadioCard } from 'compositions/ui/radio-card'
export const RadioCardWithAddon = () => {
return (
Select framework
{items.map((item) => (
))}
)
}
const items = [
{ value: 'next', title: 'Next.js', description: 'Best for apps' },
{ value: 'vite', title: 'Vite', description: 'Best for SPAs' },
{ value: 'astro', title: 'Astro', description: 'Best for static sites' },
]
```
## Props
### Root
# Radio
```tsx
'use client'
import { HStack, RadioGroup } from '@chakra-ui/react'
import { Radio } from 'compositions/ui/radio'
export const RadioBasic = () => {
return (
Option 1Option 2Option 3
)
}
```
## Anatomy
```jsx
import { Radio } from '#components/ui/radio'
import { RadioGroup } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Controlled
Use the `value` and `onValueChange` prop to control the selected radio button
```tsx
'use client'
import { useState } from 'react'
import { HStack, RadioGroup } from '@chakra-ui/react'
import { Radio } from 'compositions/ui/radio'
export const RadioControlled = () => {
const [value, setValue] = useState('1')
return (
setValue(value)}
>
Option 1Option 2Option 3
)
}
```
### Colors
Use the `colorPalette` prop to change the color scheme of the component.
```tsx
'use client'
import { HStack, Stack, Text, RadioGroup } from '@chakra-ui/react'
import { Radio } from 'compositions/ui/radio'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from '../lib/color-palettes'
export const RadioWithColors = () => {
return (
{["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"].map((colorPalette) => (
{colorPalette}ReactVueSolid
))}
)
}
```
### Sizes
Use the `size` prop to change the size of the radio component.
```tsx
'use client'
import { HStack, RadioGroup } from '@chakra-ui/react'
import { Radio } from 'compositions/ui/radio'
export const RadioWithSizes = () => {
return (
Radio (sm)Radio (md)Radio (lg)
)
}
```
### Variants
Use the `variant` prop to change the appearance of the radio component.
```tsx
'use client'
import { For, Stack, RadioGroup } from '@chakra-ui/react'
import { Radio } from 'compositions/ui/radio'
export const RadioWithVariants = () => {
return (
{(variant) => (
Radio ({variant})
Vue ({variant})
)}
)
}
```
### Disabled
Use the `disabled` prop to make the radio disabled.
```tsx
'use client'
import { HStack, RadioGroup } from '@chakra-ui/react'
import { Radio } from 'compositions/ui/radio'
export const RadioDisabled = () => {
return (
Option 1
Option 2Option 3
)
}
```
### Hook Form
Use the `Controller` component from `react-hook-form` to control the radio group
withing a form
```tsx
'use client'
import { zodResolver } from '@hookform/resolvers/zod'
import { Button, Fieldset, HStack, RadioGroup } from '@chakra-ui/react'
import { Radio } from 'compositions/ui/radio'
import { Controller, useForm } from 'react-hook-form'
import { z } from 'zod'
const items = [
{ value: '1', label: 'Option 1' },
{ value: '2', label: 'Option 2' },
{ value: '3', label: 'Option 3' },
]
const formSchema = z.object({
value: z.string({ message: 'Value is required' }),
})
type FormValues = z.infer
export const RadioWithHookForm = () => {
const {
control,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(formSchema),
})
const onSubmit = handleSubmit((data) => console.log(data))
return (
)
}
```
## Props
### Root
# Radiomark
```tsx
import { HStack, Radiomark } from '@chakra-ui/react'
export const RadiomarkBasic = () => {
return (
)
}
```
## Anatomy
```tsx
import { Radiomark } from '@chakra-ui/react'
```
```tsx
```
## Usage
`Radiomark` is the presentational dot used inside
[Radio](/docs/components/radio) and [Radio Card](/docs/components/radio-card).
It renders a `span` and has no behavior of its own — it does not select, and it
is not focusable.
Reach for it when you need the look of a radio without the interaction, for
example to show the chosen option in a summary or inside a custom control where
you manage state yourself. For anything a user can select, use `Radio` instead,
so you get the input, label association and keyboard handling.
## Examples
### States
The component supports `checked` and unchecked states, each of which can be
combined with `disabled`.
```tsx
import { HStack, Radiomark, Stack, Text } from '@chakra-ui/react'
export const RadiomarkStates = () => {
return (
Default
Disabled
)
}
```
### Variants
Use the `variant` prop to change the visual style.
```tsx
import { For, HStack, Radiomark } from '@chakra-ui/react'
export const RadiomarkVariants = () => {
return (
{(variant) => }
)
}
```
### Sizes
Use the `size` prop to change the size.
```tsx
import { For, HStack, Radiomark } from '@chakra-ui/react'
export const RadiomarkWithSizes = () => {
return (
{(size) => }
)
}
```
### Colors
Use the `colorPalette` prop to change the color scheme.
```tsx
import { For, HStack, Radiomark } from '@chakra-ui/react'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from 'compositions/lib/color-palettes'
export const RadiomarkWithColors = () => {
return (
{(colorPalette) => (
)}
)
}
```
## Props
# Rating
```tsx
import { RatingGroup } from '@chakra-ui/react'
export const RatingBasic = () => {
return (
)
}
```
## Anatomy
```tsx
import { RatingGroup } from '@chakra-ui/react'
```
```tsx
```
If you don't need to customize the rating icons, render `RatingGroup.Control`
without children. It renders the number of rating items specified in the `count`
prop.
```tsx
```
## Examples
### Basic
Use the `count` prop to set the number of rating items, and `defaultValue` to
set the initial rating.
```tsx
import { RatingGroup } from '@chakra-ui/react'
export const RatingBasic = () => {
return (
)
}
```
### Sizes
Use the `size` prop to change the size of the rating component.
```tsx
import { For, RatingGroup, Stack } from '@chakra-ui/react'
export const RatingWithSizes = () => {
return (
{(size) => (
)}
)
}
```
### Controlled
Use the `value` and `onValueChange` prop to control the rating value.
```tsx
'use client'
import { useState } from 'react'
import { RatingGroup } from '@chakra-ui/react'
export const RatingControlled = () => {
const [value, setValue] = useState(3)
return (
setValue(e.value)}
>
)
}
```
### Read Only
Use the `readOnly` prop to make the rating component read-only. This is useful
when displaying an existing rating.
```tsx
import { RatingGroup } from '@chakra-ui/react'
export const RatingWithReadonly = () => {
return (
)
}
```
### Half Star
Use the `allowHalf` prop to allow half-star ratings.
```tsx
import { RatingGroup } from '@chakra-ui/react'
export const RatingWithHalf = () => {
return (
)
}
```
### Custom Icon
Use the `icon` prop on `RatingGroup.ItemIndicator` to pass a custom icon to the
rating component. This will override the default star icon.
```tsx
import { RatingGroup } from '@chakra-ui/react'
import { IoHeart } from 'react-icons/io5'
export const RatingWithCustomIcon = () => {
return (
{Array.from({ length: 5 }).map((_, index) => (
} />
))}
)
}
```
### Testimonial
Use the rating component to show testimonials.
```tsx
import { Avatar, HStack, RatingGroup, Stack, Text } from '@chakra-ui/react'
export const RatingInTestimonial = () => {
return (
Saas UI saved us months of work. The components are well thought out and
a joy to build with.
Matthew JonesCTO, Company
)
}
```
## Props
### Root
### Item
# Rich Text Editor
```tsx
'use client'
import TextAlign from '@tiptap/extension-text-align'
import { useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { Control, RichTextEditor } from 'compositions/ui/rich-text-editor'
export const RichTextEditorBasic = () => {
const editor = useEditor({
extensions: [
StarterKit.configure({ link: { openOnClick: false } }),
TextAlign.configure({ types: ['paragraph', 'heading'] }),
],
content: `
Welcome to Saas UI + Tiptap!
Edit this document using the toolbar above.
`,
shouldRerenderOnTransaction: true,
immediatelyRender: false,
})
if (!editor) return null
return (
)
}
```
## Usage
The Rich Text Editor is built on [Tiptap](https://tiptap.dev), a headless editor
on top of ProseMirror. Install the component with the CLI, then add Tiptap and
the extensions you want to use.
```bash
pnpm dlx @saas-ui/cli@rc add rich-text-editor
```
```bash
pnpm add @tiptap/react @tiptap/starter-kit
```
```jsx
import { Control, RichTextEditor } from '#components/ui/rich-text-editor'
```
Create the editor with Tiptap's `useEditor` hook and pass it to
`RichTextEditor.Root`. Everything below the root reads the editor from context,
so the toolbar controls need no props.
```tsx
import { useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
function Editor() {
const editor = useEditor({
extensions: [StarterKit],
content: '
Hello
',
shouldRerenderOnTransaction: true,
immediatelyRender: false,
})
if (!editor) return null
return (
)
}
```
:::info
Set `shouldRerenderOnTransaction` so the controls follow the selection, and
`immediatelyRender: false` to avoid a hydration mismatch in server rendered
apps.
:::
### Anatomy
```tsx
```
`ControlGroup` groups related controls, and the toolbar draws a separator
between the groups.
### Controls
`Control` is a namespace of ready-made controls. Each one maps to a Tiptap
command and reflects the current selection, so `Control.Bold` renders as active
while the cursor sits inside bold text.
| Control | Requires |
| ---------------------------------------------------------------------------- | ------------------------------ |
| `Bold` `Italic` `Underline` `Strikethrough` `Code` | `@tiptap/starter-kit` |
| `H1` `H2` `H3` `H4` `TextStyle` `BulletList` `OrderedList` `Blockquote` `Hr` | `@tiptap/starter-kit` |
| `Link` `Unlink` | `@tiptap/starter-kit` |
| `Undo` `Redo` | `@tiptap/starter-kit` |
| `AlignLeft` `AlignCenter` `AlignRight` `AlignJustify` | `@tiptap/extension-text-align` |
| `Highlight` | `@tiptap/extension-highlight` |
A control only works when its extension is registered on the editor. The base
set comes from `StarterKit`, the rest are opt-in.
### Custom controls
Use the control factories to add your own. They take care of the tooltip, the
icon button and reading the editor from context.
```tsx
import { LuListChecks } from 'react-icons/lu'
import { createBooleanControl } from '#components/ui/rich-text-editor'
const ToggleTaskList = createBooleanControl({
label: 'Toggle Task List',
icon: LuListChecks,
command: (editor) => editor.chain().focus().toggleTaskList().run(),
getVariant: (editor) => (editor.isActive('taskList') ? 'subtle' : 'ghost'),
})
```
`createSelectControl` builds a dropdown for mutually exclusive states like the
block type, and `createSwatchControl` builds a color picker popover. For a
one-off button, render `Control.ButtonControl` directly and read the editor with
`useRichTextEditorContext`.
## Examples
### Controlled
Pass `onUpdate` to keep the document in React state. `editor.getHTML()` returns
the serialized document, `editor.getJSON()` returns the ProseMirror node.
```tsx
'use client'
import { useState } from 'react'
import { Box, Stack } from '@chakra-ui/react'
import { useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { Control, RichTextEditor } from 'compositions/ui/rich-text-editor'
export const RichTextEditorControlled = () => {
const [content, setContent] = useState('
Edit here...
')
const editor = useEditor({
shouldRerenderOnTransaction: true,
immediatelyRender: false,
extensions: [StarterKit.configure({ link: { openOnClick: false } })],
content,
onUpdate({ editor }) {
setContent(editor.getHTML())
},
})
if (!editor) return null
return (
{content}
)
}
```
### Placeholder
Add the `Placeholder` extension from `@tiptap/extensions` to show a hint while
the document is empty.
```tsx
'use client'
import { Placeholder } from '@tiptap/extensions'
import { useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { Control, RichTextEditor } from 'compositions/ui/rich-text-editor'
export const RichTextEditorWithPlaceholder = () => {
const editor = useEditor({
extensions: [
StarterKit,
Placeholder.configure({
placeholder: 'Start typing your content here...',
}),
],
content: '',
shouldRerenderOnTransaction: true,
immediatelyRender: false,
})
if (!editor) return null
return (
)
}
```
### Task list
The editor styles task lists out of the box. Add `@tiptap/extension-task-list`
and `@tiptap/extension-task-item`, then build the controls with
`createBooleanControl`.
```tsx
'use client'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import { useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import {
RichTextEditor,
createBooleanControl,
} from 'compositions/ui/rich-text-editor'
import { LuArrowLeft, LuArrowRight, LuListChecks, LuPlus } from 'react-icons/lu'
export const RichTextEditorWithTask = () => {
const editor = useEditor({
extensions: [StarterKit, TaskList, TaskItem.configure({ nested: true })],
content: `
`,
)
.run(),
getVariant: (editor) => (editor.isActive('taskItem') ? 'subtle' : 'ghost'),
})
```
### Images
Add `@tiptap/extension-image` and insert images with `setImage`. This example
combines a URL field and a file upload in a dialog.
```tsx
'use client'
import { useState } from 'react'
import { Box, Icon, Tabs } from '@chakra-ui/react'
import Image from '@tiptap/extension-image'
import { useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
import { FileUpload } from 'compositions/ui/file-upload'
import { Input } from 'compositions/ui/input'
import {
Control,
RichTextEditor,
useRichTextEditorContext,
} from 'compositions/ui/rich-text-editor'
import { LuImage, LuLink, LuUpload } from 'react-icons/lu'
export const RichTextEditorWithImage = () => {
const editor = useEditor({
content: `
Release notes
Drop an image straight into the document, or embed one from a URL.
`,
extensions: [StarterKit, Image],
shouldRerenderOnTransaction: true,
immediatelyRender: false,
})
if (!editor) return null
return (
)
}
function InsertImageControl() {
const { editor } = useRichTextEditorContext()
const [open, setOpen] = useState(false)
const [url, setUrl] = useState('')
const [files, setFiles] = useState([])
if (!editor) return null
return (
<>
}
label="Insert Image"
variant="ghost"
onClick={() => setOpen(true)}
/>
setOpen(e.open)}>
Insert Image Embed URL
Upload File
setUrl(e.target.value)}
/>
{
const uploaded = accepted.files ?? []
setFiles(uploaded)
if (uploaded[0]) {
const objectUrl = URL.createObjectURL(uploaded[0])
editor.chain().focus().setImage({ src: objectUrl }).run()
setOpen(false)
}
}}
>
Drag and drop a file here.png, .jpg up to 5MB
>
)
}
```
### Bubble menu
Wrap a toolbar in Tiptap's `BubbleMenu` to float the controls above the
selection. Use the `floating` toolbar variant so it renders as a panel.
```tsx
'use client'
import { useEditor } from '@tiptap/react'
import { BubbleMenu } from '@tiptap/react/menus'
import StarterKit from '@tiptap/starter-kit'
import { Control, RichTextEditor } from 'compositions/ui/rich-text-editor'
export const RichTextEditorWithBubbleMenu = () => {
const editor = useEditor({
extensions: [StarterKit.configure({ link: { openOnClick: false } })],
content: sampleContent,
shouldRerenderOnTransaction: true,
immediatelyRender: false,
})
if (!editor) return null
return (
)
}
const sampleContent = `
Select some text to see the bubble menu
The Bold, Italic, Underline and Strikethrough controls appear right above the selection.
Try selecting text within this list item.
Use the list buttons to switch between bullet and ordered lists.
`
```
## Props
### Root
### Toolbar
### Content
### ButtonControl
### createBooleanControl
### createSelectControl
### createSwatchControl
# Segmented Control
```tsx
'use client'
import { SegmentedControl } from 'compositions/ui/segmented-control'
export const SegmentedControlBasic = () => {
return (
)
}
```
## Anatomy
```jsx
import { SegmentedControl } from '#components/ui/segmented-control'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the segmented control.
```tsx
'use client'
import { For, Stack, Text, VStack } from '@chakra-ui/react'
import { SegmentedControl } from 'compositions/ui/segmented-control'
export const SegmentedControlWithSizes = () => {
return (
{(size) => (
size = {size}
)}
)
}
```
### Controlled
Use the `value` and `onValueChange` props to control the selected item.
```tsx
'use client'
import { useState } from 'react'
import { SegmentedControl } from 'compositions/ui/segmented-control'
export const SegmentedControlControlled = () => {
const [value, setValue] = useState('React')
return (
e.value && setValue(e.value)}
items={['React', 'Vue', 'Solid']}
/>
)
}
```
### Hook Form
Here's an example of how to use the `SegmentedControl` with `react-hook-form`.
```tsx
'use client'
import { zodResolver } from '@hookform/resolvers/zod'
import { Button, Stack, Field } from '@chakra-ui/react'
import { SegmentedControl } from 'compositions/ui/segmented-control'
import { Controller, useForm } from 'react-hook-form'
import { z } from 'zod'
const formSchema = z.object({
fontSize: z.string({ message: 'Font size is required' }),
})
type FormValues = z.infer
export const SegmentedControlWithHookForm = () => {
const {
handleSubmit,
formState: { errors },
control,
} = useForm({
defaultValues: { fontSize: 'md' },
resolver: zodResolver(formSchema),
})
const onSubmit = handleSubmit((data) => console.log(data))
return (
)
}
```
### Disabled
Use the `disabled` prop to disable the segmented control.
```tsx
'use client'
import { SegmentedControl } from 'compositions/ui/segmented-control'
export const SegmentedControlWithDisabled = () => {
return (
)
}
```
### Disabled Item
Use the `disabled` prop on the item to disable it.
```tsx
'use client'
import { SegmentedControl } from 'compositions/ui/segmented-control'
export const SegmentedControlWithDisabledItem = () => {
return (
)
}
```
### Icon
Render the `label` as a `ReactNode` to render an icon.
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { SegmentedControl } from 'compositions/ui/segmented-control'
import { LuGrid2X2, LuList, LuTable } from 'react-icons/lu'
export const SegmentedControlWithIcon = () => {
return (
Table
),
},
{
value: 'board',
label: (
Board
),
},
{
value: 'list',
label: (
List
),
},
]}
/>
)
}
```
### Card
Here's an example of how to use the `SegmentedControl` within a `Card`.
```tsx
'use client'
import { Button, Card, Heading, Field } from '@chakra-ui/react'
import { SegmentedControl } from 'compositions/ui/segmented-control'
import { LuSearch } from 'react-icons/lu'
export const SegmentedControlInCard = () => {
return (
Find your dream homeBedroomsBedsBathrooms
)
}
```
## Props
### Root
# Select
```tsx
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectBasic = () => {
return (
Select framework
{frameworks.items.map((movie) => (
{movie.label}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
```
## Anatomy
```jsx
import { Select } from '#components/ui/select'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the select component.
```tsx
'use client'
import { For, Stack, createListCollection } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectWithSizes = () => {
return (
{(size) => (
size = {size}
{frameworks.items.map((movie) => (
{movie.label}
))}
)}
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
```
### Variants
Use the `variant` prop to change the appearance of the select component.
```tsx
'use client'
import { For, Stack, createListCollection } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectWithVariants = () => {
return (
{(variant) => (
Select framework - {variant}
{frameworks.items.map((movie) => (
{movie.label}
))}
)}
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
```
### Option Group
Use the `SelectItemGroup` component to group select options.
```tsx
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectWithOptionGroup = () => {
return (
Select framework
{categories.map((category) => (
{category.items.map((item) => (
{item.label}
))}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: 'Naruto', value: 'naruto', group: 'Anime' },
{ label: 'One Piece', value: 'one-piece', group: 'Anime' },
{ label: 'Dragon Ball', value: 'dragon-ball', group: 'Anime' },
{
label: 'The Shawshank Redemption',
value: 'the-shawshank-redemption',
group: 'Movies',
},
{ label: 'The Godfather', value: 'the-godfather', group: 'Movies' },
{ label: 'The Dark Knight', value: 'the-dark-knight', group: 'Movies' },
],
})
const categories = frameworks.items.reduce(
(acc, item) => {
const group = acc.find((group) => group.group === item.group)
if (group) {
group.items.push(item)
} else {
acc.push({ group: item.group, items: [item] })
}
return acc
},
[] as { group: string; items: (typeof frameworks)['items'] }[],
)
```
### Controlled
Use the `value` and `onValueChange` props to control the select component.
```tsx
'use client'
import { useState } from 'react'
import { createListCollection } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectControlled = () => {
const [value, setValue] = useState([])
return (
setValue(e.value)}
>
Select framework
{frameworks.items.map((movie) => (
{movie.label}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
```
### Hook Form
Here's an example of how to use the `Select` component with `react-hook-form`.
```tsx
'use client'
import { zodResolver } from '@hookform/resolvers/zod'
import { Button, Stack, createListCollection, Field } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
import { Controller, useForm } from 'react-hook-form'
import { z } from 'zod'
const formSchema = z.object({
framework: z.string({ message: 'Framework is required' }).array(),
})
type FormValues = z.infer
export const SelectWithHookForm = () => {
const {
handleSubmit,
formState: { errors },
control,
} = useForm({
resolver: zodResolver(formSchema),
})
const onSubmit = handleSubmit((data) => console.log(data))
return (
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
```
### Disabled
Use the `disabled` prop to disable the select component.
```tsx
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectWithDisabled = () => {
return (
Select framework
{frameworks.items.map((movie) => (
{movie.label}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
```
### Invalid
Use the `invalid` prop to indicate that the select component has an error.
```tsx
'use client'
import { createListCollection, Field } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectWithInvalid = () => {
return (
Select framework
{frameworks.items.map((movie) => (
{movie.label}
))}
This is an error
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
```
### Positioning
Use the `positioning` prop to control the underlying `floating-ui` options of
the select component.
```tsx
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectWithPositioning = () => {
return (
Select framework
{frameworks.items.map((movie) => (
{movie.label}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
```
### Within Popover
Use the `Select` within a `Popover` component.
```tsx
'use client';
import { createListCollection, Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
import { Select } from 'compositions/ui/select'
export const SelectInPopover = () => {
return (
Select in Popover
{frameworks.items.map((item) => (
{item.label}
))}
)
}
const frameworks = createListCollection({
items: [
{ label: 'React.js', value: 'react' },
{ label: 'Vue.js', value: 'vue' },
{ label: 'Angular', value: 'angular' },
{ label: 'Svelte', value: 'svelte' },
],
})
```
### Avatar Select
Here's an example of how to compose the `Select` and the `Avatar`.
```tsx
'use client';
import { HStack, createListCollection } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
import { Select } from 'compositions/ui/select'
const SelectValueItem = () => (
{(items: Array<{ name: string; avatar: string }>) => {
const { name, avatar } = items[0]
return (
{name}
)
}}
)
export const SelectWithAvatar = () => {
return (
Select member
{members.items.map((item) => (
{item.name}
))}
)
}
const members = createListCollection({
items: [
{
name: 'Jessica Jones',
id: 'jessica_jones',
avatar:
'https://images.unsplash.com/photo-1531746020798-e6953c6e8e04?w=100',
},
{
name: 'Kenneth Johnson',
id: 'kenneth_johnson',
avatar:
'https://images.unsplash.com/photo-1523477800337-966dbabe060b?w=100',
},
{
name: 'Kate Wilson',
id: 'kate_wilson',
avatar:
'https://images.unsplash.com/photo-1609712409631-dbbb050746d1?w=100',
},
],
itemToString: (item) => item.name,
itemToValue: (item) => item.id,
})
```
### Clear Trigger
Pass the `clearable` prop to the `SelectTrigger`.
```tsx
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectWithClear = () => {
return (
Select fav. anime
{animeMovies.items.map((movie) => (
{movie.label}
))}
)
}
const animeMovies = createListCollection({
items: [
{ label: 'Spirited Away', value: 'spirited_away' },
{ label: 'My Neighbor Totoro', value: 'my_neighbor_totoro' },
{ label: 'Akira', value: 'akira' },
{ label: 'Princess Mononoke', value: 'princess_mononoke' },
{ label: 'Grave of the Fireflies', value: 'grave_of_the_fireflies' },
{ label: "Howl's Moving Castle", value: 'howls_moving_castle' },
{ label: 'Ghost in the Shell', value: 'ghost_in_the_shell' },
{ label: 'Naruto', value: 'naruto' },
{ label: 'Hunter x Hunter', value: 'hunter_x_hunter' },
{ label: 'The Wind Rises', value: 'the_wind_rises' },
{ label: "Kiki's Delivery Service", value: 'kikis_delivery_service' },
{ label: 'Perfect Blue', value: 'perfect_blue' },
{
label: 'The Girl Who Leapt Through Time',
value: 'the_girl_who_leapt_through_time',
},
{ label: 'Weathering with You', value: 'weathering_with_you' },
{ label: 'Ponyo', value: 'ponyo' },
{ label: '5 Centimeters per Second', value: '5_centimeters_per_second' },
{ label: 'A Silent Voice', value: 'a_silent_voice' },
{ label: 'Paprika', value: 'paprika' },
{ label: 'Wolf Children', value: 'wolf_children' },
{ label: 'Redline', value: 'redline' },
{
label: 'The Tale of the Princess Kaguya',
value: 'the_tale_of_the_princess_kaguya',
},
],
})
```
### Overflow
```tsx
'use client'
import { createListCollection } from '@chakra-ui/react'
import { Select } from 'compositions/ui/select'
export const SelectWithOverflow = () => {
return (
Select anime
{animeMovies.items.map((movie) => (
{movie.label}
))}
)
}
const animeMovies = createListCollection({
items: [
{ label: 'Spirited Away', value: 'spirited_away' },
{ label: 'My Neighbor Totoro', value: 'my_neighbor_totoro' },
{ label: 'Akira', value: 'akira' },
{ label: 'Princess Mononoke', value: 'princess_mononoke' },
{ label: 'Grave of the Fireflies', value: 'grave_of_the_fireflies' },
{ label: "Howl's Moving Castle", value: 'howls_moving_castle' },
{ label: 'Ghost in the Shell', value: 'ghost_in_the_shell' },
{ label: 'Naruto', value: 'naruto' },
{ label: 'Hunter x Hunter', value: 'hunter_x_hunter' },
{ label: 'The Wind Rises', value: 'the_wind_rises' },
{ label: "Kiki's Delivery Service", value: 'kikis_delivery_service' },
{ label: 'Perfect Blue', value: 'perfect_blue' },
{
label: 'The Girl Who Leapt Through Time',
value: 'the_girl_who_leapt_through_time',
},
{ label: 'Weathering with You', value: 'weathering_with_you' },
{ label: 'Ponyo', value: 'ponyo' },
{ label: '5 Centimeters per Second', value: '5_centimeters_per_second' },
{ label: 'A Silent Voice', value: 'a_silent_voice' },
{ label: 'Paprika', value: 'paprika' },
{ label: 'Wolf Children', value: 'wolf_children' },
{ label: 'Redline', value: 'redline' },
{
label: 'The Tale of the Princess Kaguya',
value: 'the_tale_of_the_princess_kaguya',
},
],
})
```
## Props
### Root
# Slider
```tsx
'use client'
import { Slider } from 'compositions/ui/slider'
export const SliderBasic = () => {
return
}
```
## Anatomy
```jsx
import { Slider } from '#components/ui/slider'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the slider.
```tsx
'use client'
import { Stack } from '@chakra-ui/react'
import { Slider } from 'compositions/ui/slider'
export const SliderWithSizes = () => {
return (
)
}
```
### Variants
Use the `variant` prop to change the visual style of the slider.
```tsx
'use client'
import { Stack } from '@chakra-ui/react'
import { Slider } from 'compositions/ui/slider'
export const SliderWithVariants = () => {
return (
)
}
```
### Colors
Use the `colorPalette` prop to change the color of the slider.
```tsx
'use client'
import { Stack } from '@chakra-ui/react'
import { Slider } from 'compositions/ui/slider'
export const SliderWithColors = () => {
return (
)
}
```
### Label
Use the `label` prop to add a label to the slider.
```tsx
'use client'
import { Slider } from 'compositions/ui/slider'
export const SliderWithLabel = () => {
return
}
```
### Range Slider
Set the `value` or `defaultValue` prop to an array to create a range slider.
```tsx
'use client'
import { Slider } from 'compositions/ui/slider'
export const SliderWithMultipleThumbs = () => {
return
}
```
### Controlled
Use the `value` and `onValueChange` props to control the value of the slider.
```tsx
'use client'
import { useState } from 'react'
import { Slider } from 'compositions/ui/slider'
export const SliderControlled = () => {
const [value, setValue] = useState([40])
return (
setValue(e.value)}
/>
)
}
```
### Hook Form
Here's an example of how to integrate a slider with `react-hook-form`.
```tsx
'use client'
import { zodResolver } from '@hookform/resolvers/zod'
import { Stack, Button, Field } from '@chakra-ui/react'
import { Slider } from 'compositions/ui/slider'
import { Controller, useForm } from 'react-hook-form'
import { z } from 'zod'
const formSchema = z.object({
value: z.array(
z
.number({ message: 'Value is required' })
.min(60, { message: 'Value must be greater than 60' }),
),
})
type FormValues = z.infer
export const SliderWithHookForm = () => {
const {
control,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(formSchema),
defaultValues: { value: [40] },
})
const onSubmit = handleSubmit((data) => console.log(data))
return (
)
}
```
### Disabled
Use the `disabled` prop to disable the slider.
```tsx
'use client'
import { Slider } from 'compositions/ui/slider'
export const SliderDisabled = () => {
return
}
```
### Change End
Use the `onValueChangeEnd` prop to listen to the end of the slider change.
```tsx
'use client'
import { useState } from 'react'
import { Box, Code, Stack } from '@chakra-ui/react'
import { Slider } from 'compositions/ui/slider'
const initialValue = [50]
export const SliderChangeEnd = () => {
const [value, setValue] = useState(initialValue)
const [endValue, setEndValue] = useState(initialValue)
return (
setValue(e.value)}
onValueChangeEnd={(e) => setEndValue(e.value)}
/>
onChange: {value}
onChangeEnd: {endValue}
)
}
```
### Steps
Use the `step` prop to set the step value of the slider.
```tsx
'use client'
import { Slider } from 'compositions/ui/slider'
export const SliderWithStep = () => {
return
}
```
### Thumb Contained
Use the `thumbAlignment` and `thumbSize` prop to contain the thumb within the
track.
```tsx
'use client'
import { Slider } from 'compositions/ui/slider'
export const SliderThumbContained = () => {
return (
)
}
```
### Marks
Use the `marks` prop to display marks on the slider.
```tsx
'use client'
import { For, Stack, Text, VStack } from '@chakra-ui/react'
import { Slider } from 'compositions/ui/slider'
export const SliderWithMarks = () => {
return (
{(size) => (
size = {size}
)}
)
}
```
### Vertical
Use the `orientation` prop to change the orientation of the slider.
```tsx
'use client'
import { Slider } from 'compositions/ui/slider'
export const SliderVertical = () => {
return
}
```
## Props
# Switch
```tsx
'use client'
import { Switch } from 'compositions/ui/switch'
export const SwitchBasic = () => {
return Activate Chakra
}
```
## Anatomy
```jsx
import { Switch } from '#components/ui/switch'
```
```jsx
Label
```
## Examples
### Sizes
Use the `size` prop to change the size of the switch component.
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { Switch } from 'compositions/ui/switch'
export const SwitchWithSizes = () => {
return (
)
}
```
### Variants
Use the `variant` prop to change the visual style of the switch.
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { Switch } from 'compositions/ui/switch'
export const SwitchWithVariants = () => {
return (
)
}
```
### Colors
Use the `colorPalette` prop to change the color scheme of the component.
```tsx
'use client'
import { Stack, Text } from '@chakra-ui/react'
import { Switch } from 'compositions/ui/switch'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from '../lib/color-palettes'
export const SwitchWithColors = () => {
return (
{["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"].map((colorPalette) => (
{colorPalette}
))}
)
}
```
### Controlled
Use the `checked` and `onCheckedChange` prop to control the state of the switch.
```tsx
'use client'
import { useState } from 'react'
import { Switch } from 'compositions/ui/switch'
export const SwitchControlled = () => {
const [checked, setChecked] = useState(false)
return (
setChecked(e.checked)} />
)
}
```
### Hook Form
Here's an example of integrating the switch with `react-hook-form`.
```tsx
'use client'
import { zodResolver } from '@hookform/resolvers/zod'
import { Button, Stack, Field } from '@chakra-ui/react'
import { Switch } from 'compositions/ui/switch'
import { Controller, useForm } from 'react-hook-form'
import { z } from 'zod'
const formSchema = z.object({
active: z.boolean({ message: 'Active is required' }),
})
type FormData = z.infer
export const SwitchWithHookForm = () => {
const {
handleSubmit,
control,
formState: { errors },
} = useForm({
resolver: zodResolver(formSchema),
})
return (
)
}
```
### Disabled
Use the `disabled` prop to disable the switch.
```tsx
'use client'
import { Switch } from 'compositions/ui/switch'
export const SwitchWithDisabled = () => {
return Activate Chakra
}
```
### Invalid
Use the `invalid` prop to indicate an error state for the switch.
```tsx
'use client'
import { Switch } from 'compositions/ui/switch'
export const SwitchWithInvalid = () => {
return Activate Chakra
}
```
### Tooltip
Here's an example of a switch with a tooltip.
```tsx
'use client';
import { useId } from 'react'
import { Switch } from 'compositions/ui/switch'
import { Tooltip } from 'compositions/ui/tooltip'
export const SwitchWithTooltip = () => {
const id = useId()
return (
Switch with tooltip
)
}
```
### Track Label
Use the `trackLabel` prop to display different label based on the checked state.
```tsx
'use client'
import { Icon } from '@chakra-ui/react'
import { Switch } from 'compositions/ui/switch'
import { FaMoon, FaSun } from 'react-icons/fa'
export const SwitchWithTrackLabel = () => {
return (
),
off: (
),
}}
/>
)
}
```
### Thumb Label
Use the `thumbLabel` prop to add an icon to the switch thumb.
```tsx
'use client'
import { Switch } from 'compositions/ui/switch'
import { HiCheck, HiX } from 'react-icons/hi'
export const SwitchWithThumbLabel = () => {
return (
, off: }}>
Switch me
)
}
```
## Props
### Root
# Tags Input
```tsx
'use client'
import { Span, TagsInput } from '@chakra-ui/react'
export const TagsInputBasic = () => {
return (
Tags
Press Enter or Return to add tag
)
}
```
## Anatomy
```jsx
import { TagsInput } from '@chakra-ui/react'
```
```jsx
```
If you don't need to customize the individual tags, use the `TagsInput.Items`
shortcut. It renders all tag items automatically based on the current value.
```jsx
```
## Examples
### Basic
Tags are created when the user presses `Enter` or `Return`, and removed with
`Backspace` or the delete trigger on each tag.
```tsx
'use client'
import { Span, TagsInput } from '@chakra-ui/react'
export const TagsInputBasic = () => {
return (
Tags
Press Enter or Return to add tag
)
}
```
### Sizes
Use the `size` prop to adjust the size of the tags input.
```tsx
'use client'
import { For, Stack, TagsInput } from '@chakra-ui/react'
export const TagsInputWithSizes = () => {
return (
{(size) => (
Tags (size={size})
)}
)
}
```
### Variants
Use the `variant` prop to change the visual style of the tags input.
```tsx
'use client'
import { For, Stack, TagsInput } from '@chakra-ui/react'
export const TagsInputWithVariants = () => {
return (
{(variant) => (
Tags (variant={variant})
)}
)
}
```
### Controlled
Use the `value` and `onValueChange` props to programmatically control the tags.
```tsx
'use client'
import { useState } from 'react'
import { TagsInput } from '@chakra-ui/react'
export const TagsInputControlled = () => {
const [tags, setTags] = useState(['React', 'Saas UI'])
return (
setTags(details.value)}
>
Tags
)
}
```
### Max Tags
Pass the `max` prop to the `TagsInput.Root` component to limit the number of
tags that can be added. Combine it with the `validate` prop to only accept tags
that pass your own validation rules.
```tsx
'use client'
import { Badge, Button, HStack, Span, TagsInput } from '@chakra-ui/react'
const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const isValidEmail = (value: string) => EMAIL_REGEX.test(value)
export const TagsInputWithMax = () => {
return (
isValidEmail(e.inputValue)}
defaultValue={['sage@company.com']}
>
Invite guests (max 3)
{({ value }) => (
You've invited {value.length} / 3 guests to
your event
)}
)
}
```
### Editable Tags
Use the `editable` prop to enable inline editing of existing tags by double
clicking or pressing `Enter` when highlighted, allowing users to quickly update
tag values.
```tsx
'use client'
import { Span, TagsInput } from '@chakra-ui/react'
export const TagsInputEditable = () => {
return (
Edit tags inline
Use the arrow keys to navigate and press Enter to edit
)
}
```
### Field
Compose the `TagsInput` component with the `Field` component to add helper text
and error messages. This example also uses the `delimiter` and `addOnPaste`
props so users can paste a comma separated list of emails.
```tsx
'use client'
import { Field, TagsInput } from '@chakra-ui/react'
export const TagsInputWithField = () => {
return (
Invite team membersAdd emails separated by commas
)
}
```
## Props
### Root
### Item
# Textarea
```tsx
'use client'
import { Textarea } from '@chakra-ui/react'
export const TextareaBasic = () => {
return
}
```
## Anatomy
```jsx
import { Textarea } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Variants
Use the `variant` prop to change the appearance of the textarea.
```tsx
import { Stack, Textarea } from '@chakra-ui/react'
export const TextareaWithVariants = () => {
return (
)
}
```
### Sizes
Use the `size` prop to change the size of the textarea.
```tsx
import { Stack, Textarea } from '@chakra-ui/react'
export const TextareaWithSizes = () => {
return (
)
}
```
### Helper Text
Pair the textarea with the `Field` component to add helper text.
```tsx
'use client';
import { HStack, Field, Textarea } from '@chakra-ui/react'
export const TextareaWithHelperText = () => {
return (
CommentMax 500 characters.CommentMax 500 characters.
)
}
```
### Error Text
Pair the textarea with the `Field` component to add error text.
```tsx
'use client';
import { HStack, Field, Textarea } from '@chakra-ui/react'
export const TextareaWithErrorText = () => {
return (
CommentField is requiredCommentField is required
)
}
```
### Field
Compose the textarea with the `Field` component to add a label, helper text, and
error text.
```tsx
'use client';
import { HStack, Field, Input } from '@chakra-ui/react'
export const InputWithField = () => {
return (
Email
)
}
```
### Hook Form
Here's an example of how to integrate the textarea with `react-hook-form`.
```tsx
'use client';
import { Input, Stack, Button, Field, Textarea } from '@chakra-ui/react'
import { useForm } from 'react-hook-form'
interface FormValues {
username: string
bio: string
}
export const TextareaWithHookForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
const onSubmit = handleSubmit((data) => console.log(data))
return (
)
}
```
### Resize
Use the `resize` prop to control the resize behavior of the textarea.
```tsx
import { Stack, Textarea } from '@chakra-ui/react'
export const TextareaWithResize = () => {
return (
)
}
```
### Autoresize
Here's an example of how to integrate the `react-textarea-autosize` package to
make the textarea autoresize.
```tsx
'use client'
import { Textarea } from '@chakra-ui/react'
export const TextareaWithAutoresize = () => {
return
}
```
## Props
# Toggle
```tsx
import { Button, Toggle } from '@chakra-ui/react'
import { LuBold } from 'react-icons/lu'
export const ToggleBasic = () => {
return (
)
}
```
## Anatomy
```tsx
import { Toggle } from '@chakra-ui/react'
```
```tsx
```
The `Toggle.Root` renders a `button` element. Use the `asChild` prop to render
it as any other button component, like the `Button` component.
## Examples
### Basic
Use the `_pressed` condition to style the toggle when it is pressed. Since the
toggle renders a button, the `asChild` prop lets you reuse the `Button`
component and its variants.
```tsx
import { Button, Toggle } from '@chakra-ui/react'
import { LuBold } from 'react-icons/lu'
export const ToggleBasic = () => {
return (
)
}
```
### Indicator
Use the `Toggle.Indicator` component to render different content based on the
pressed state. Pass the unpressed content to the `fallback` prop, and the
pressed content as children.
```tsx
import { Button, Toggle } from '@chakra-ui/react'
import { LuVolume2, LuVolumeX } from 'react-icons/lu'
export const ToggleWithIndicator = () => {
return (
)
}
```
### Pressed Icon
Render a `Toggle.Indicator` without a `fallback` to only show an icon when the
toggle is pressed. The `colorPalette` prop also accepts the `_pressed`
condition.
```tsx
import { Button, Toggle } from '@chakra-ui/react'
import { LuX } from 'react-icons/lu'
export const ToggleWithPressedIcon = () => {
return (
)
}
```
### Controlled
Use the `pressed` and `onPressedChange` props to control the pressed state
programmatically.
```tsx
'use client'
import { useState } from 'react'
import { Button, Code, Stack, Toggle } from '@chakra-ui/react'
export const ToggleControlled = () => {
const [pressed, setPressed] = useState(false)
return (
Pressed: {pressed ? 'true' : 'false'}
)
}
```
## Props
### Root
### Indicator
# AbsoluteCenter
```tsx
import { AbsoluteCenter, Box } from '@chakra-ui/react'
export const AbsoluteCenterBasic = () => {
return (
Centered Content
)
}
```
## Usage
The `AbsoluteCenter` component uses the `position: absolute` strategy to center
its child element relative to its parent.
> The parent element must have `position: relative` for proper positioning.
```jsx
import { AbsoluteCenter } from '@chakra-ui/react'
```
```jsx
Centered Content
```
## Examples
### Axis Control
Control which axis to center on using the `axis` prop. Options are `horizontal`,
`vertical`, or `both` (default).
```tsx
import { AbsoluteCenter, Box, For, Text, VStack } from '@chakra-ui/react'
export const AbsoluteCenterWithAxis = () => {
return (
{(axis) => (
{``}
{axis}
)}
)
}
```
### With Content
Use `AbsoluteCenter` with various content types like icons, badges, and status
indicators.
```tsx
import { AbsoluteCenter, Box } from '@chakra-ui/react'
import { LuHeart } from 'react-icons/lu'
export const AbsoluteCenterWithContent = () => {
return (
)
}
```
### Overlay Usage
Perfect for creating loading overlays or modal-like content that needs to be
centered over existing content.
```tsx
import { AbsoluteCenter, Box, HStack, Spinner, Text } from '@chakra-ui/react'
const Overlay = () => (
Loading...
)
export const AbsoluteCenterWithOverlay = () => {
return (
Some content that is being loaded...
)
}
```
### RTL Support
`AbsoluteCenter` automatically handles right-to-left (RTL) layouts by adjusting
the horizontal positioning and transforms appropriately.
```tsx
import {
AbsoluteCenter,
Box,
For,
HStack,
Span,
Text,
VStack,
} from '@chakra-ui/react'
export const AbsoluteCenterWithRtl = () => {
return (
{(axis) => (
RTL ({axis})البداية
)}
)
}
```
## Props
# Aspect Ratio
```tsx
import { AspectRatio, Center } from "@chakra-ui/react"
export const AspectRatioBasic = () => {
return (
2 / 1
)
}
```
## Usage
```jsx
import { AspectRatio } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Image
Here's how to embed an image that has a 4 by 3 aspect ratio.
```tsx
import { AspectRatio, Image } from "@chakra-ui/react"
export const AspectRatioWithImage = () => {
return (
)
}
```
### Video
To embed a video with a specific aspect ratio, use an iframe with `src` pointing
to the link of the video.
```tsx
import { AspectRatio } from "@chakra-ui/react"
export const AspectRatioWithVideo = () => {
return (
)
}
```
### Google Map
Here's how to embed a responsive Google map using `AspectRatio`.
```tsx
import { AspectRatio } from "@chakra-ui/react"
export const AspectRatioWithMap = () => {
return (
)
}
```
### Responsive
Here's an example of applying a responsive aspect ratio to a box.
```tsx
import { AspectRatio } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const AspectRatioResponsive = () => (
Box
)
```
## Props
These props can be passed to the `AspectRatio` component.
# Bleed
```tsx
import { Bleed, Box, Heading, Stack, Text } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const BleedBasic = () => {
return (
BleedSome HeadingLorem ipsum dolor sit amet, consectetur adipiscing elit.
)
}
```
## Usage
```jsx
import { Bleed } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Vertical
Use the `block` prop to make the element bleed vertically.
```tsx
import { Bleed, Box } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const BleedVertical = () => {
return (
Bleed
)
}
```
### Specific Direction
Use the `inlineStart`, `inlineEnd`, `blockStart`, and `blockEnd` props to make
the element bleed in a specific direction.
```tsx
import { Bleed, Box, Stack } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const BleedWithDirection = () => {
return (
inlineStartinlineEndblockStartblockEnd
)
}
```
## Props
# Box
```tsx
import { Box } from "@chakra-ui/react"
export const BoxBasic = () => {
return (
This is the Box
)
}
```
## Usage
The `Box` component provides an easy way to write styles with ease. It provides
access to design tokens and an unmatched DX when writing responsive styles.
```jsx
import { Box } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Shorthand
Use shorthand like `bg` instead of `backgroundColor`, `m` instead of `margin`,
etc.
```tsx
import { Box } from "@chakra-ui/react"
export const BoxWithShorthand = () => {
return (
This is the Box
)
}
```
### Psuedo Props
Use pseudo props like `_hover` to apply styles on hover, `_focus` to apply
styles on focus, etc.
```tsx
import { Box } from "@chakra-ui/react"
export const BoxWithPseudoProps = () => {
return (
This is the Box
)
}
```
### Border
Use the `borderWidth` and `borderColor` prop to apply border styles.
> **Good to know:** Chakra applies `borderStyle: solid` globally so you don't
> have to.
```tsx
import { Box } from "@chakra-ui/react"
export const BoxWithBorder = () => {
return (
Somewhat disabled box
)
}
```
### As Prop
Use the `as` prop to render a different component.
> Inspect the DOM to see the rendered component.
```tsx
import { Box } from "@chakra-ui/react"
export const BoxWithAsProp = () => {
return (
This is a Box rendered as a section
)
}
```
### Shadow
Use the `boxShadow` or `shadow` prop to apply shadow styles.
```tsx
import { Box } from "@chakra-ui/react"
export const BoxWithShadow = () => {
return (
Box with shadow
)
}
```
### Composition
Here's an example of a property card built with layout primitives in Chakra.
```tsx
import { Badge, Box, HStack, Icon, Image, Text } from "@chakra-ui/react"
import { HiStar } from "react-icons/hi"
export const BoxPropertyCard = () => {
return (
Superhost
{data.rating} ({data.reviewCount})
{data.title}
{data.formattedPrice} • {data.beds} beds
)
}
const data = {
imageUrl: "https://bit.ly/2Z4KKcF",
imageAlt: "Rear view of modern home with pool",
beds: 3,
title: "Modern home in city center in the heart of historic Los Angeles",
formattedPrice: "$435",
reviewCount: 34,
rating: 4.5,
}
```
## Props
The `Box` component supports all CSS properties as props, making it easy to
style elements.
# Center
```tsx
import { Box, Center } from "@chakra-ui/react"
export const CenterBasic = () => {
return (
This will be centered
)
}
```
## Usage
```jsx
import { AbsoluteCenter, Center, Circle, Square } from "@chakra-ui/react"
```
```jsx
This is the Center
```
## Examples
### Icon
Center can be used to create frames around icons or numbers.
```tsx
import { Box, Center, HStack } from "@chakra-ui/react"
import { LuPhone } from "react-icons/lu"
export const CenterWithIcons = () => {
return (
1
)
}
```
### Center with Inline
Use the `inline` to change the display to `inline-flex`.
```tsx
import { Box, Center, Link } from "@chakra-ui/react"
import { LuArrowRight } from "react-icons/lu"
export const CenterWithInline = () => {
return (
Visit Chakra UI
)
}
```
### Square
`Square` centers its child given the `size` (width and height).
```tsx
import { Square } from "@chakra-ui/react"
import { LuPhoneForwarded } from "react-icons/lu"
export const CenterWithSquare = () => {
return (
)
}
```
### Circle
`Circle` centers its child given the `size` and creates a circle around it.
```tsx
import { Circle } from "@chakra-ui/react"
import { LuPhoneForwarded } from "react-icons/lu"
export const CenterWithCircle = () => {
return (
)
}
```
### AbsoluteCenter
`AbsoluteCenter` centers relative to its parent using the `position: absolute`
strategy. Pass the `axis` prop to change the axis of alignment.
```tsx
import { AbsoluteCenter, Box } from "@chakra-ui/react"
import { LuPhone } from "react-icons/lu"
export const CenterWithAbsolute = () => {
return (
)
}
```
## Props
### AbsoluteCenter
# Container
```tsx
'use client'
import { Container } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const ContainerBasic = () => {
return (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
consectetur, tortor in lacinia eleifend, dui nisl tristique nunc.
)
}
```
## Usage
The default `maxWidth` is `8xl` which maps to `90rem (1440px)`.
```jsx
import { Container } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Sizes
Use the `maxWidth` prop to change the size of the container.
```tsx
'use client'
import { Container, For, Stack } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const ContainerWithSizes = () => {
return (
{(size) => (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
consectetur, tortor in lacinia eleifend, dui nisl tristique nunc.
)}
)
}
```
### Fluid
Use the `fluid` prop to make the container stretch to fill the width of its
parent.
```tsx
'use client'
import { Container } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const ContainerWithFluid = () => {
return (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
consectetur, tortor in lacinia eleifend, dui nisl tristique nunc.
)
}
```
## Props
# Flex
```tsx
import { Flex } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const FlexBasic = () => {
return (
)
}
```
## Usage
```jsx
import { Flex } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Direction
Use the `direction` or `flexDirection` prop to change the direction of the flex
```tsx
import { Flex } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const FlexWithDirection = () => {
return (
)
}
```
### Align
Use the `align` or `alignItems` prop to align the children along the cross axis.
```tsx
import { Flex } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const FlexWithAlign = () => {
return (
)
}
```
### Justify
Use the `justify` or `justifyContent` prop to align the children along the main
axis.
```tsx
import { Flex } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const FlexWithJustify = () => {
return (
flex-start
center
flex-end
space-between
)
}
```
### Order
Use the `order` prop to change the order of the children.
```tsx
import { Flex } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const FlexWithOrder = () => {
return (
1
2
3
)
}
```
### Auto Margin
Apply margin to a flex item to push it away from its siblings.
```tsx
import { Flex } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const FlexWithAutoMargin = () => {
return (
)
}
```
### Wrap
Use the `wrap` or `flexWrap` prop to wrap the children when they overflow the
parent.
```tsx
import { Flex } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const FlexWithWrap = () => {
return (
)
}
```
## Props
# Float
```tsx
import { Box, Circle, Float } from "@chakra-ui/react"
export const FloatBasic = () => (
3
)
```
## Usage
Float requires a parent element with `position: relative` style applied.
```jsx
import { Box, Float } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Placement
Use the `placement` prop to position the element along the edges of the
container.
```tsx
import { Box, Circle, Float, HStack, Stack } from "@chakra-ui/react"
export const FloatWithPlacements = () => (
{placements.map((placement) => (
{placement}
3
))}
)
const placements = [
"bottom-end",
"bottom-start",
"top-end",
"top-start",
"bottom-center",
"top-center",
"middle-center",
"middle-end",
"middle-start",
] as const
```
### Offset X
Use the `offsetX` prop to offset the element along the x-axis.
```tsx
import { Box, Circle, Float } from "@chakra-ui/react"
export const FloatWithOffsetX = () => (
3
)
```
### Offset Y
Use the `offsetY` prop to offset the element along the y-axis.
```tsx
import { Box, Circle, Float } from "@chakra-ui/react"
export const FloatWithOffsetY = () => (
3
)
```
### Offset
Use the `offset` prop to offset the element along both axes.
```tsx
import { Box, Circle, Float } from "@chakra-ui/react"
export const FloatWithOffset = () => (
3
)
```
### Avatar
Here's an example of composing a `Float` component with an `Avatar` component.
```tsx
'use client'
import { Box, Float, Badge } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
export const FloatWithAvatar = () => {
return (
New
)
}
```
## Props
# Grid
```tsx
import { Grid } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const GridBasic = () => {
return (
)
}
```
## Usage
```jsx
import { Grid, GridItem } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Col Span
Pass `colSpan` prop to `GridItem` to span across columns.
```tsx
import { Grid, GridItem } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const GridWithColSpan = () => {
return (
)
}
```
### Spanning Columns
In some layouts, you may need certain grid items to span specific amount of
columns or rows instead of an even distribution
```tsx
import { Grid, GridItem } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const GridSpanningColumns = () => {
return (
rowSpan=2colSpan=2colSpan=2colSpan=4
)
}
```
## Props
# Group
```tsx
import { Group } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const GroupBasic = () => {
return (
1
2
)
}
```
## Usage
```jsx
import { Group } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Button
Here's an example of using the `Group` component to group buttons together.
```tsx
import { Button, Group } from "@chakra-ui/react"
export const GroupWithButton = () => {
return (
)
}
```
### Attached
Use the `attached` prop to attach the children together.
```tsx
import { Badge, Button, Group, Stack } from "@chakra-ui/react"
export const GroupWithAttached = () => {
return (
Commit status
90+
)
}
```
### Grow
Use the `grow` prop to make the children grow to fill the available space.
```tsx
import { Button, Group } from "@chakra-ui/react"
export const GroupWithGrow = () => {
return (
)
}
```
## Props
# Scroll Area
```tsx
import { ScrollArea } from '@chakra-ui/react'
import LoremIpsum from 'react-lorem-ipsum'
export const ScrollAreaBasic = () => (
)
```
## Anatomy
```jsx
import { ScrollArea } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Variants
Use the `variant` prop to change the scrollbar visibility behavior. Values can
be either `hover` (default) or `always`.
```tsx
import { For, ScrollArea, Stack, Text } from '@chakra-ui/react'
import Lorem from 'react-lorem-ipsum'
const variants = ['hover', 'always'] as const
export const ScrollAreaWithVariants = () => (
{(variant) => (
variant="{variant}"
)}
)
```
### Sizes
Use the `size` prop to change the size of the scroll area. This affects the
scrollbar thickness and content padding.
```tsx
import { For, ScrollArea, Stack, Text } from '@chakra-ui/react'
import LoremIpsum from 'react-lorem-ipsum'
const sizes = ['xs', 'sm', 'md', 'lg'] as const
export const ScrollAreaWithSizes = () => (
{(size) => (
size="{size}"
)}
)
```
### Horizontal Scrolling
The scroll area automatically supports horizontal scrolling when content
overflows horizontally.
```tsx
import { Flex, ScrollArea } from '@chakra-ui/react'
import { Box } from 'compositions/lib/decorative-box'
export const ScrollAreaHorizontal = () => (
{Array.from({ length: 12 }, (_, i) => (
Item {i + 1}
))}
)
```
### Both Directions
When content overflows in both directions, both scrollbars will appear.
```tsx
import { ScrollArea } from '@chakra-ui/react'
import LoremIpsum from 'react-lorem-ipsum'
export const ScrollAreaBothDirections = () => (
)
```
:::info
You can render the `ScrollArea.Corner` component to show a corner indicator to
fill the intersection of the two scrollbars for a seamless, styled appearance.
:::
### Scroll Shadow
Add visual feedback when content is scrollable by implementing scroll shadows
that appear at the edges using `mask-image`.
Use the `data-overflow-y` attribute to only show shadows when content actually
overflows—this prevents shadows from appearing when there's a single item or no
scrollable content.
```tsx
import { ScrollArea } from '@chakra-ui/react'
import { Box } from 'compositions/lib/decorative-box'
export const ScrollAreaWithScrollShadow = () => {
return (
{Array.from({ length: 10 }, (_, i) => (
Item {i + 1}
))}
)
}
```
### Thumb Styling
Customize the appearance of the scrollbar thumb with different styles and
colors.
```tsx
import { ScrollArea } from '@chakra-ui/react'
import LoremIpsum from 'react-lorem-ipsum'
export const ScrollAreaWithThumbStyling = () => (
)
```
## Props
### Root
### Viewport
### Content
### Scrollbar
### Thumb
### Corner
# SimpleGrid
```tsx
import { SimpleGrid } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const SimpleGridBasic = () => {
return (
)
}
```
## Usage
The `SimpleGrid` component allows you to create responsive grid layouts with
ease.
```jsx
import { SimpleGrid } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Columns
Specify the number of columns for the grid layout using the `columns` prop.
```tsx
import { SimpleGrid } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const SimpleGridWithColumns = () => (
)
```
### Auto-responsive
Make the grid responsive and adjust automatically without passing columns, by
using the `minChildWidth` prop. This uses css grid auto-fit and minmax()
internally.
```tsx
import { SimpleGrid } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const SimpleGridWithAutofit = () => (
)
```
### Column Span
Specify the size of the column by using the `colSpan` prop.
```tsx
import { GridItem, SimpleGrid } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const SimpleGridWithColSpan = () => (
Column 1Column 2
)
```
### Row and Column Gap
Pass the `rowGap` and `columnGap` props to change the row and column spacing
between the grid items.
```tsx
import { SimpleGrid } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const SimpleGridWithRowAndColGap = () => {
return (
)
}
```
# Splitter
```tsx
import { Center, Splitter } from '@chakra-ui/react'
export const SplitterBasic = () => {
return (
A
B
)
}
```
## Anatomy
```jsx
import { Splitter } from '@chakra-ui/react'
```
```jsx
```
The `Splitter.ResizeTrigger` renders the `Splitter.ResizeTriggerSeparator` and
`Splitter.ResizeTriggerIndicator` for you, so writing
`` is enough unless you need to customize the
separator or indicator.
## Examples
### Controlled
Use the `size` and `onResize` props to manage panel sizes programmatically.
```tsx
setSizes(details.size)}
>
{/* ... */}
```
```tsx
'use client'
import { useState } from 'react'
import { Center, Code, HStack, Span, Splitter, Stack } from '@chakra-ui/react'
import { LuMouse, LuMoveHorizontal } from 'react-icons/lu'
export const SplitterControlled = () => {
const [sizes, setSizes] = useState([50, 50])
return (
Drag the handle to resize panels setSizes(details.size)}
borderWidth="1px"
minH="60"
>
A
B
Panel A: {sizes[0].toFixed(1)}% | Panel B: {sizes[1].toFixed(1)}%
)
}
```
### Vertical
Pass the `orientation="vertical"` prop to the `Splitter.Root` component for
stacked panels that resize vertically.
```tsx
import { Center, Splitter } from '@chakra-ui/react'
export const SplitterVertical = () => {
return (
A
B
)
}
```
### Multiple Panels
Create layouts with more than two resizable panels by passing an array of panels
to the `panels` prop of the `Splitter.Root` component.
```tsx
import { Center, Splitter } from '@chakra-ui/react'
export const SplitterMultiplePanels = () => {
return (
A
B
C
)
}
```
### Collapsible Panels
Make the panels collapsible and snapped to a specific size by setting the
`collapsible` and `collapsedSize` properties on a panel in the `panels` array.
```tsx
{/* ... */}
```
```tsx
import { Box, Center, HStack, Splitter } from '@chakra-ui/react'
import { LuMouse, LuMoveHorizontal } from 'react-icons/lu'
export const SplitterCollapsible = () => {
return (
Drag the resizer to collapse or expand Panel A
A
B
)
}
```
### Min/Max Constraints
Set `minSize` and `maxSize` on panels to constrain their resizable range and
prevent resizing beyond these boundaries.
```tsx
'use client'
import { useState } from 'react'
import { Center, Code, HStack, Span, Splitter, Stack } from '@chakra-ui/react'
import { LuMouse, LuMoveHorizontal } from 'react-icons/lu'
export const SplitterMinMaxConstraints = () => {
const [sizes, setSizes] = useState([30, 70])
return (
Drag to resize - Panel A: 20-60%, Panel B: min 40% setSizes(details.size)}
minH="60"
>
A
{sizes[0].toFixed(1)}% (min: 20%, max: 60%)
B
{sizes[1].toFixed(1)}% (min: 40%)
)
}
```
### Nested Panels
Nest splitters inside panels to create more complex layouts. Each nested
splitter can have its own orientation, sizes, and behaviors independent of the
parent splitter.
```tsx
import { Center, Splitter } from '@chakra-ui/react'
export const SplitterNested = () => {
return (
A
B1
B2
)
}
```
### Storage
Set a `defaultSize` and pair it with a storage solution, such as
`useLocalStorage`, to save users' panel size preferences. This ensures that
panel layouts persist across sessions. Alternatively, you can use cookies or
other storage mechanisms depending on your needs.
```tsx
'use client'
import {
Badge,
Button,
Center,
HStack,
Span,
Splitter,
Stack,
} from '@chakra-ui/react'
import { LuBox, LuMouse, LuMoveHorizontal, LuTrash2 } from 'react-icons/lu'
import { useLocalStorage } from 'react-use'
export const SplitterWithStorage = () => {
const [sizes, setSizes] = useLocalStorage('splitter-sizes', [70, 50])
const formattedSizes = sizes?.map((size) => size.toFixed(1)).join(', ')
const hasSavedState = sizes && sizes.length > 0
const clearStorage = () => {
setSizes(undefined)
}
return (
Drag to resize panels
{hasSavedState && (
)}
setSizes(e.size)}
borderWidth="1px"
minH="60"
>
A
B
LocalStorage{' '}
{hasSavedState ? `[${formattedSizes}]` : '[Not saved]'}
)
}
```
## Props
### Root
### Panel
### ResizeTrigger
# Stack
```tsx
import { Stack } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const StackBasic = () => {
return (
)
}
```
## Usage
By default, Stack applies `flex-direction: column` and `gap: 8px` to its
children.
```jsx
import { HStack, Stack, VStack } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Horizontal
Use the `direction` prop to change the direction of the stack.
```tsx
import { Stack } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const StackHorizontal = () => {
return (
)
}
```
### HStack
Alternatively, you can use the `HStack` to create a horizontal stack and align
its children horizontally.
```tsx
import { HStack } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const StackWithHstack = () => {
return (
)
}
```
### VStack
Use the `VStack` to create a vertical stack and align its children vertically.
```tsx
import { VStack } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const StackWithVstack = () => {
return (
)
}
```
### Separator
Use the `separator` prop to add a separator between the stack items.
```tsx
import { Stack, StackSeparator } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const StackWithSeparator = () => {
return (
}>
)
}
```
### Responsive Direction
Use the `direction` prop to change the direction of the stack responsively.
```tsx
import { Stack } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const StackWithResponsiveDirection = () => {
return (
)
}
```
# Wrap
```tsx
import { Badge, Wrap } from '@chakra-ui/react'
export const WrapBasic = () => (
Badge 1Badge 2Badge 3
)
```
## Usage
By default, `Wrap` applies `display: flex`, `flex-wrap: wrap`, and `gap: 8px` to
its children.
```jsx
import { Wrap, WrapItem } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Gap or Spacing
Pass the `gap` prop to apply a consistent spacing between each child, even if it
wraps.
```tsx
import { Wrap } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const WrapWithGap = () => (
{Array.from({ length: 10 }).map((_, index) => (
))}
)
```
### Alignment
Pass the `align` prop to change the alignment of the child along the cross axis.
```tsx
import { Center, Wrap, WrapItem } from '@chakra-ui/react'
export const WrapWithAlign = () => (
{Array.from({ length: 5 }).map((_, index) => (
Box {index + 1}
))}
)
```
### Justify
Pass the `justify` prop to change the alignment of the child along the main
axis.
```tsx
import { Center, Wrap, WrapItem } from '@chakra-ui/react'
export const WrapWithJustify = () => (
{Array.from({ length: 5 }).map((_, index) => (
Box {index + 1}
))}
)
```
### Row and Column Gap
Pass the `rowGap` and `columnGap` props to apply a consistent spacing between
the rows and columns. These props accept responsive values as well.
```tsx
import { Wrap } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
export const WrapWithRowColumnGap = () => (
{Array.from({ length: 10 }).map((_, index) => (
))}
)
```
# Avatar
```tsx
'use client'
import { Avatar } from 'compositions/ui/avatar'
export const AvatarBasic = () => {
return
}
```
## Anatomy
```jsx
import { Avatar, AvatarGroup } from '#components/ui/avatar'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the avatar
```tsx
import { HStack } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
export const AvatarWithSizes = () => {
return (
)
}
```
### Shape
Use the `shape` prop to change the shape of the avatar, from `rounded` to
`square`
```tsx
import { HStack } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
export const AvatarWithShape = () => {
return (
)
}
```
### Fallback
The initials of the name can be used as a fallback when the `src` prop is not
provided or when the image fails to load.
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
export const AvatarWithFallback = () => {
return (
)
}
```
### Ring
Use the `outline*` props to add a ring around the avatar
```tsx
import { HStack } from '@chakra-ui/react'
import { defineStyle } from '@chakra-ui/react/styled-system'
import { Avatar } from 'compositions/ui/avatar'
const ringCss = defineStyle({
outlineWidth: '2px',
outlineColor: 'colorPalette.500',
outlineOffset: '2px',
outlineStyle: 'solid',
})
export const AvatarWithRing = () => {
return (
)
}
```
### Group
Use the `Group` component to group multiple avatars together
```tsx
import { Avatar, AvatarGroup } from 'compositions/ui/avatar'
export const AvatarWithGroup = () => {
return (
)
}
```
### Stacking
When using the `AvatarGroup` component, you can use the `stacking` prop to
change the stacking order of the avatars
```tsx
'use client'
import { Stack } from '@chakra-ui/react'
import { Avatar, AvatarGroup } from 'compositions/ui/avatar'
export const AvatarGroupWithStacking = () => {
return (
{items.map((item) => (
))}
{items.map((item) => (
))}
{items.map((item) => (
))}
)
}
const items = [
{
src: '/img/avatars/1.png',
name: 'David Wilson',
},
{
src: '/avatars/2.png',
name: 'Marcus Chen',
},
{
src: '/avatars/3.png',
name: 'Sarah Johnson',
},
]
```
## Props
### Root
### Fallback
### Image
# Carousel
```tsx
'use client'
import { Carousel, IconButton } from '@chakra-ui/react'
import { LuChevronLeft, LuChevronRight } from 'react-icons/lu'
import { Box } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselBasic = () => {
return (
{items.map((_, index) => (
{index + 1}
))}
)
}
```
## Anatomy
```jsx
import { Carousel } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Controlled
Use the `page` and `onPageChange` props to programmatically control the active
carousel page.
```tsx
'use client'
import { useState } from 'react'
import { Carousel, IconButton } from '@chakra-ui/react'
import { LuChevronLeft, LuChevronRight } from 'react-icons/lu'
import { Box } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselControlled = () => {
const [page, setPage] = useState(0)
return (
setPage(e.page)}
>
{items.map((_, index) => (
{index + 1}
))}
)
}
```
### Arrows
Use the `Carousel.PrevTrigger` and `Carousel.NextTrigger` components to create
arrows that navigate between slides.
```tsx
'use client'
import { Carousel, IconButton } from '@chakra-ui/react'
import { LuArrowLeft, LuArrowRight } from 'react-icons/lu'
import { Box } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselWithFloatingArrow = () => {
return (
{items.map((_, index) => (
{index + 1}
))}
)
}
```
### Indicators
Use the `Carousel.Indicators` component to render visual indicators that help
users track the progress of the carousel and jump to specific slides.
It's a shortcut that renders a full set of indicators based on the number of
slides, so you don't have to map over them yourself.
```jsx
{Array.from({ length: items.length }, (_, index) => (
))}
```
Use `Carousel.IndicatorGroup` and `Carousel.Indicator` directly when you need to
customize each indicator.
```tsx
'use client'
import { Carousel } from '@chakra-ui/react'
import { Box } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselWithIndicators = () => {
return (
{items.map((_, index) => (
{index + 1}
))}
)
}
```
### Spacing
Use the `spacing` prop to control the spacing between slides.
```tsx
'use client'
import { Carousel, HStack, IconButton } from '@chakra-ui/react'
import { LuChevronLeft, LuChevronRight } from 'react-icons/lu'
import { Box } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselSpacing = () => {
return (
{"spacing='48px'"}
{items.map((_, index) => (
{index + 1}
))}
)
}
```
### Vertical
Set the `orientation` prop to `vertical` to transform your carousel into a
vertical slider.
```tsx
'use client'
import { Carousel, IconButton } from '@chakra-ui/react'
import { LuChevronDown, LuChevronUp } from 'react-icons/lu'
import { Box } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselVertical = () => {
return (
{items.map((_, index) => (
{index + 1}
))}
)
}
```
### Autoplay
Pass the `autoplay` prop to the `Carousel.Root` component to make the carousel
automatically move between slides.
```tsx
'use client'
import { Carousel, HStack, IconButton } from '@chakra-ui/react'
import {
LuChevronLeft,
LuChevronRight,
LuClock,
LuPause,
LuPlay,
} from 'react-icons/lu'
import { Box } from '../lib/decorative-box'
const items = Array.from({ length: 5 })
export const CarouselWithAutoplay = () => {
return (
{'autoplay={{ delay: 2000 }}'} or {'autoplay={true}'}
{items.map((_, index) => (
{index + 1}
))}
}
play={}
/>
)
}
```
### Images
Here's an example that shows how to create an image carousel for a product
showcase.
```tsx
'use client'
import { forwardRef } from 'react'
import type { IconButtonProps } from '@chakra-ui/react'
import { AspectRatio, Box, Carousel, IconButton, Image } from '@chakra-ui/react'
import { LuArrowLeft, LuArrowRight } from 'react-icons/lu'
export const CarouselWithImages = () => {
return (
{items.map((src, index) => (
))}
)
}
const ActionButton = forwardRef(
function ActionButton(props, ref) {
return (
)
},
)
const items = [
'https://images.unsplash.com/photo-1656433031375-5042f5afe894?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2371',
'https://images.unsplash.com/photo-1587466412525-87497b34fc88?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2673',
'https://images.unsplash.com/photo-1629581688635-5d88654e5bdd?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2831',
'https://images.unsplash.com/photo-1661030420948-862787de0056?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2370',
'https://images.unsplash.com/photo-1703505841379-2f863b201212?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2371',
'https://images.unsplash.com/photo-1607776905497-b4f788205f6a?ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&q=80&w=2370',
]
```
## Props
### Root
### ItemGroup
### Item
### Control
# IconBadge
```tsx
import { Icon } from '@chakra-ui/react'
import { HiHeart } from 'react-icons/hi'
export const IconBasic = () => (
)
```
## Anatomy
```jsx
import { IconBadge } from '#components/ui/icon-badge'
```
```jsx
```
:::warning
When rendering an icon as a child, the component needs to have a ref forwarded.
:::
## Built-in Icons
Saas UI uses Lucide icons by default, but you can use any icon library.
```jsx
import { LuChevronUp } from 'react-icons/lu'
```
## Examples
### React Icons
Integrate with popular react icon libraries like `react-icons`
```tsx
import { Icon } from "@chakra-ui/react"
import { Md3dRotation } from "react-icons/md"
export const IconWithReactIcon = () => (
)
```
### Custom svg
Use the `asChild` prop to render custom svg icons within the `Icon` component
```tsx
import { Icon } from "@chakra-ui/react"
export const IconWithCustomSvg = () => {
return (
)
}
```
### Create Icon
Use the `createIcon` utility to create custom icons
```tsx
"use client"
import { createIcon } from "@chakra-ui/react"
const HeartIcon = createIcon({
displayName: "HeartIcon",
path: (
<>
>
),
})
export const IconWithCreateIcon = () => (
)
```
# Icon
```tsx
import { Icon } from '@chakra-ui/react'
import { HiHeart } from 'react-icons/hi'
export const IconBasic = () => (
)
```
## Anatomy
```jsx
import { Icon } from '@chakra-ui/react'
```
```jsx
```
:::warning
When rendering an icon as a child, the component needs to have a ref forwarded.
:::
## Built-in Icons
Saas UI uses Lucide icons by default, but you can use any icon library.
```jsx
import { LuChevronUp } from 'react-icons/lu'
```
## Examples
### React Icons
Integrate with popular react icon libraries like `react-icons`
```tsx
import { Icon } from "@chakra-ui/react"
import { Md3dRotation } from "react-icons/md"
export const IconWithReactIcon = () => (
)
```
### Custom svg
Use the `asChild` prop to render custom svg icons within the `Icon` component
```tsx
import { Icon } from "@chakra-ui/react"
export const IconWithCustomSvg = () => {
return (
)
}
```
### Create Icon
Use the `createIcon` utility to create custom icons
```tsx
"use client"
import { createIcon } from "@chakra-ui/react"
const HeartIcon = createIcon({
displayName: "HeartIcon",
path: (
<>
>
),
})
export const IconWithCreateIcon = () => (
)
```
# Image
```tsx
'use client'
import { Image } from '@chakra-ui/react'
export const ImageBasic = () => (
)
```
## Anatomy
```js
import { Image } from '@chakra-ui/react'
```
```jsx
```
## Examples
### Height
Use the `height` prop to change the height of the image component.
```tsx
'use client'
import { Image } from '@chakra-ui/react'
export const ImageWithHeight = () => {
return (
)
}
```
### Circular
Here's an example of how to render a circular image.
```tsx
'use client'
import { Image } from '@chakra-ui/react'
export const ImageCircular = () => (
)
```
### Aspect Ratio
Use the `aspectRatio` prop to change the aspect ratio of the image component.
```tsx
'use client'
import { Image } from '@chakra-ui/react'
export const ImageWithAspectRatio = () => {
return (
)
}
```
### Fit
By default, the image applies `object-fit: cover`. Use the `fit` prop to change
the fit of the image component.
```tsx
'use client'
import { Image } from '@chakra-ui/react'
export const ImageWithFit = () => (
)
```
### Next.js Image
Use the `asChild` prop to render the image as a child of the `Image` component.
```jsx
// import NextImage from "next/image"
```
## Props
The `Image` component supports all native props for the `img` element.
# Marquee
```tsx
'use client'
import { Marquee } from '@chakra-ui/react'
import {
IoLogoFigma,
IoLogoGitlab,
IoLogoJavascript,
IoLogoLinkedin,
IoLogoTwitter,
IoLogoVimeo,
} from 'react-icons/io5'
export const MarqueeBasic = () => (
{items.map((item, i) => (
))}
)
const items = [
{ icon: IoLogoFigma, label: 'Figma', color: '#F24E1E' },
{ icon: IoLogoTwitter, label: 'Twitter', color: '#1da1f2' },
{ icon: IoLogoLinkedin, label: 'LinkedIn', color: '#0077b5' },
{ icon: IoLogoGitlab, label: 'GitLab', color: '#fc6d26' },
{ icon: IoLogoVimeo, label: 'Vimeo', color: '#1ab7ea' },
{ icon: IoLogoJavascript, label: 'JavaScript', color: '#f7df1e' },
]
```
## Anatomy
```jsx
import { Marquee } from '@chakra-ui/react'
```
```jsx
```
Use the `autoFill` prop to repeat the items until the viewport is filled, and
the `spacing` prop to control the gap between the repeated groups.
## Examples
### Reversed
Use the `reverse` prop on the `Marquee.Root` component to reverse the direction
of the marquee.
```tsx
'use client'
import { Marquee } from '@chakra-ui/react'
import {
IoLogoFigma,
IoLogoGitlab,
IoLogoJavascript,
IoLogoLinkedin,
IoLogoTwitter,
IoLogoVimeo,
} from 'react-icons/io5'
export const MarqueeReverseDirection = () => (
{items.map((item, i) => (
))}
)
const items = [
{ icon: IoLogoFigma, label: 'Figma', color: '#F24E1E' },
{ icon: IoLogoTwitter, label: 'Twitter', color: '#1da1f2' },
{ icon: IoLogoLinkedin, label: 'LinkedIn', color: '#0077b5' },
{ icon: IoLogoGitlab, label: 'GitLab', color: '#fc6d26' },
{ icon: IoLogoVimeo, label: 'Vimeo', color: '#1ab7ea' },
{ icon: IoLogoJavascript, label: 'JavaScript', color: '#f7df1e' },
]
```
### Vertical
To animate content along the Y axis, use the `side` prop and set it to `bottom`.
```tsx
'use client'
import { Marquee } from '@chakra-ui/react'
import {
IoLogoFigma,
IoLogoGitlab,
IoLogoJavascript,
IoLogoLinkedin,
IoLogoTwitter,
IoLogoVimeo,
} from 'react-icons/io5'
export const MarqueeVerticalAnimation = () => (
{items.map((item, i) => (
))}
)
const items = [
{ icon: IoLogoFigma, label: 'Figma', color: '#F24E1E' },
{ icon: IoLogoTwitter, label: 'Twitter', color: '#1da1f2' },
{ icon: IoLogoLinkedin, label: 'LinkedIn', color: '#0077b5' },
{ icon: IoLogoGitlab, label: 'GitLab', color: '#fc6d26' },
{ icon: IoLogoVimeo, label: 'Vimeo', color: '#1ab7ea' },
{ icon: IoLogoJavascript, label: 'JavaScript', color: '#f7df1e' },
]
```
### Speed
Use the `speed` prop to control the animation speed in pixels per second.
```tsx
'use client'
import { For, Marquee, Span, Stack } from '@chakra-ui/react'
import {
IoLogoFigma,
IoLogoGitlab,
IoLogoJavascript,
IoLogoLinkedin,
IoLogoTwitter,
IoLogoVimeo,
} from 'react-icons/io5'
const speeds = [
{ value: 25, label: 'Slow (25px/s)' },
{ value: 50, label: 'Normal (50px/s)' },
{ value: 100, label: 'Fast (100px/s)' },
]
export const MarqueeWithSpeed = () => (
{(speed) => (
{speed.label}
{items.map((item, i) => (
))}
)}
)
const items = [
{ icon: IoLogoFigma, label: 'Figma', color: '#F24E1E' },
{ icon: IoLogoTwitter, label: 'Twitter', color: '#1da1f2' },
{ icon: IoLogoLinkedin, label: 'LinkedIn', color: '#0077b5' },
{ icon: IoLogoGitlab, label: 'GitLab', color: '#fc6d26' },
{ icon: IoLogoVimeo, label: 'Vimeo', color: '#1ab7ea' },
{ icon: IoLogoJavascript, label: 'JavaScript', color: '#f7df1e' },
]
```
### Pause On Interaction
Use the `pauseOnInteraction` prop to pause the animation when the user hovers or
focuses the marquee.
```tsx
'use client'
import { Marquee } from '@chakra-ui/react'
import {
IoLogoFigma,
IoLogoGitlab,
IoLogoJavascript,
IoLogoLinkedin,
IoLogoTwitter,
IoLogoVimeo,
} from 'react-icons/io5'
export const MarqueePauseInteractions = () => (
{items.map((item, i) => (
))}
)
const items = [
{ icon: IoLogoFigma, label: 'Figma', color: '#F24E1E' },
{ icon: IoLogoTwitter, label: 'Twitter', color: '#1da1f2' },
{ icon: IoLogoLinkedin, label: 'LinkedIn', color: '#0077b5' },
{ icon: IoLogoGitlab, label: 'GitLab', color: '#fc6d26' },
{ icon: IoLogoVimeo, label: 'Vimeo', color: '#1ab7ea' },
{ icon: IoLogoJavascript, label: 'JavaScript', color: '#f7df1e' },
]
```
### Edge Gradient
Render the `Marquee.Edge` component to apply an edge fade.
```tsx
'use client'
import { Marquee } from '@chakra-ui/react'
import {
IoLogoFigma,
IoLogoGitlab,
IoLogoJavascript,
IoLogoLinkedin,
IoLogoTwitter,
IoLogoVimeo,
} from 'react-icons/io5'
export const MarqueeEdgeGradient = () => (
{items.map((item, i) => (
))}
)
const items = [
{ icon: IoLogoFigma, label: 'Figma', color: '#F24E1E' },
{ icon: IoLogoTwitter, label: 'Twitter', color: '#1da1f2' },
{ icon: IoLogoLinkedin, label: 'LinkedIn', color: '#0077b5' },
{ icon: IoLogoGitlab, label: 'GitLab', color: '#fc6d26' },
{ icon: IoLogoVimeo, label: 'Vimeo', color: '#1ab7ea' },
{ icon: IoLogoJavascript, label: 'JavaScript', color: '#f7df1e' },
]
```
### Testimonials
You can display testimonials with the marquee component.
```tsx
'use client'
import { Avatar, Box, Card, HStack, Marquee, Stack } from '@chakra-ui/react'
import { IoStar } from 'react-icons/io5'
export const MarqueeWithTestimonials = () => (
{testimonials.map((item, i) => (
))}
)
const TestimonialCard = ({ item }: { item: Testimonial }) => {
return (
{Array.from({ length: item.rating }).map((_, i) => (
))}
“{item.content}”
{item.name}
{item.role}
)
}
interface Testimonial {
name: string
role: string
rating: number
avatar: string
content: string
}
const testimonials: Testimonial[] = [
{
name: 'Sarah Chen',
role: 'Product Designer',
rating: 5,
avatar: 'https://i.pravatar.cc/150?u=a042581f4e29026024d',
content:
'This library saved me weeks of work. The components are accessible and easy to customize.',
},
{
name: 'Michael Torres',
role: 'Frontend Dev',
rating: 4,
avatar: 'https://i.pravatar.cc/150?u=a042581f4e29026704d',
content:
'The animations are buttery smooth. I love how easy it is to implement the marquee.',
},
{
name: 'Emily Wang',
role: 'CTO',
rating: 5,
avatar: 'https://i.pravatar.cc/150?u=a04258114e29026302d',
content:
'Scalable, reliable, and beautiful. Highly recommended for any modern web project.',
},
{
name: 'David Smith',
role: 'Marketing Lead',
rating: 5,
avatar: 'https://i.pravatar.cc/150?u=a042581f4e29026024d',
content:
'Our conversion rates increased by 15% after switching to this UI system.',
},
{
name: 'Jessica Lee',
role: 'Indie Hacker',
rating: 4,
avatar: 'https://i.pravatar.cc/150?u=a04258a2462d826712d',
content:
'Documentation is top-notch. I was able to build my MVP in a single weekend.',
},
]
```
### Finite Loops
To loop the marquee a finite number of times, set the `loopCount` prop on
`Marquee.Root`. Alternatively, use the `onLoopComplete` and `onComplete`
callbacks to track the number of completed loops or when the animation fully
finishes.
```tsx
{
/* handle loop completion */
}}
onComplete={() => {
/* handle animation end */
}}
>
{/* Marquee.Item elements */}
```
### CSS Variables
The marquee component exposes CSS variables that can be used to customize its
behavior and appearance.
```tsx
{/* ... */}
```
| Variable | Description | Default |
| ---------------------- | ----------------------------------- | ----------- |
| `--marquee-duration` | Animation duration | Computed |
| `--marquee-delay` | Animation delay before starting | `0s` |
| `--marquee-loop-count` | Number of animation iterations | `infinite` |
| `--marquee-edge-color` | Color for the edge gradient overlay | `colors.bg` |
| `--marquee-edge-size` | Size of the edge gradient | `20%` |
## Props
# Persona
```tsx
'use client'
import { Persona } from 'compositions/ui/persona'
export const PersonaBasic = () => {
return (
David WilsonSoftware Engineer
)
}
```
## Anatomy
```jsx
import { Persona } from '#components/ui/persona/persona-composed'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the avatar
```tsx
'use client';
import { Persona } from 'compositions/ui/persona'
import { Stack } from '@chakra-ui/react'
const sizes = ['xs', 'sm', 'md', 'lg', 'xl', '2xl'] as const
export const PersonaSizes = () => {
return (
{sizes.map((size) => (
David WilsonSoftware Engineer
))}
)
}
```
### Shape
Use the `shape` prop to change the shape of the avatar, from `rounded` to
`square`
```tsx
'use client';
import { Persona } from 'compositions/ui/persona'
import { Stack } from '@chakra-ui/react'
export const PersonaWithShape = () => {
return (
)
}
```
### Presence
Use the `presence` prop to change the presence of the avatar. Available presence
options are `online`, `busy`, `away`, `dnd`, and `offline`.
```tsx
'use client';
import { Persona } from 'compositions/ui/persona'
import { Stack } from '@chakra-ui/react'
const presenceOptions = ['online', 'busy', 'dnd', 'away', 'offline'] as const
export const PersonaWithPresence = () => {
return (
{presenceOptions.map((presence) => (
David WilsonSoftware Engineer
))}
)
}
```
### Presence Ring
The `variant` prop can be set to `ring` to add a ring around the avatar to
indicate the presence.
```tsx
'use client';
import { Persona } from 'compositions/ui/persona'
import { Stack } from '@chakra-ui/react'
const presenceOptions = ['online', 'busy', 'dnd', 'away', 'offline'] as const
export const PersonaWithPresenceRing = () => {
return (
{presenceOptions.map((presence) => (
David WilsonSoftware Engineer
))}
)
}
```
### Out of office
Use the `outOfOffice` prop to change the out of office status of the avatar
```tsx
'use client'
import { Persona } from 'compositions/ui/persona'
export const PersonaOutOfOffice = () => {
return (
David WilsonSoftware EngineerOn a road trip
)
}
```
### Ring
Use the `outline*` props to add a ring around the avatar
```tsx
'use client';
import { Persona } from 'compositions/ui/persona'
import { defineStyle } from '@chakra-ui/react/styled-system'
export const PersonaWithRing = () => {
return (
David WilsonSoftware Engineer
)
}
const ringCss = defineStyle({
outlineWidth: '2px',
outlineColor: 'var(--persona-presence)',
outlineOffset: '2px',
outlineStyle: 'solid',
})
```
## Props
### Root
### Avatar
### PresenceBadge
### Details
### Label
### SecondaryLabel
### TertiaryLabel
# Breadcrumb
```tsx
'use client'
import { Breadcrumb } from 'compositions/ui/breadcrumb'
export const BreadcrumbBasic = () => {
return (
DocsComponentsProps
)
}
```
## Anatomy
```jsx
import { Breadcrumb } from '#components/ui/breadcrumb'
```
```jsx
WorkspaceProjects
```
## Examples
### Sizes
Use the `size` prop to change the size of the breadcrumb component
```tsx
'use client'
import { Stack } from '@chakra-ui/react'
import { Breadcrumb } from 'compositions/ui/breadcrumb'
export const BreadcrumbWithSizes = () => {
return (
DocsComponentsPropsDocsComponentsPropsDocsComponentsProps
)
}
```
### Variants
Use the `variant` prop to change the appearance of the breadcrumb component
```tsx
'use client'
import { Stack } from '@chakra-ui/react'
import { Breadcrumb } from 'compositions/ui/breadcrumb'
export const BreadcrumbWithVariants = () => {
return (
DocsComponentsPropsDocsComponentsProps
)
}
```
### With Separator
Use the `separator` prop to add the separator
```tsx
'use client'
import { Breadcrumb } from 'compositions/ui/breadcrumb'
import { LiaSlashSolid } from 'react-icons/lia'
export const BreadcrumbWithSeparator = () => {
return (
}>
DocsComponentsProps
)
}
```
### Icon
Here's how you can add an icon to the breadcrumb
```tsx
'use client'
import { Breadcrumb } from 'compositions/ui/breadcrumb'
import { LuHouse, LuShirt } from 'react-icons/lu'
export const BreadcrumbWithIcon = () => {
return (
Home
Men Wear
Trousers
)
}
```
### Menu
Compose the breadcrumb with menu component
```tsx
'use client';
import { Breadcrumb } from 'compositions/ui/breadcrumb'
import { Menu } from 'compositions/ui/menu'
import { LuChevronDown } from 'react-icons/lu'
export const BreadcrumbWithMenu = () => {
return (
Docs
Components ThemePropsCustomizationProps
)
}
```
### Ellipsis
Render the `BreadcrumbEllipsis` component to show an ellipsis
```tsx
'use client'
import { Breadcrumb } from 'compositions/ui/breadcrumb'
export const BreadcrumbWithEllipsis = () => {
return (
DocsComponentsProps
)
}
```
### Routing Library
Use the `asChild` prop to change the underlying breadcrumb link
```jsx
// import { Link } from "next/link"
Docs
```
## Props
### Root
# Pagination
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
export const PaginationBasic = () => {
return (
)
}
```
## Anatomy
```jsx
import { Pagination } from '#components/ui/pagination'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the pagination.
:::info
The pagination sizes are mapped to the `Button` component sizes.
:::
```tsx
'use client'
import { For, HStack, Stack } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
export const PaginationWithSizes = () => {
return (
{(size) => (
)}
)
}
```
### Variants
Use the `variant` prop to control the variant of the pagination items and
ellipsis.
> The variant matches the `Button` component variant.
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
export const PaginationWithVariants = () => {
return (
)
}
```
### Controlled
Use the `page` and `onPageChange` props to control the current page.
```tsx
'use client'
import { useState } from 'react'
import { HStack } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
export const PaginationControlled = () => {
const [page, setPage] = useState(1)
return (
setPage(e.page)}
>
)
}
```
### Sibling Count
Use `siblingCount` to control the number of sibling pages to show before and
after the current page.
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
export const PaginationWithSiblingCount = () => {
return (
)
}
```
### Compact
Use the `PaginationPageText` to create a compact pagination. This can be useful
for mobile views.
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
export const PaginationCompact = () => {
return (
)
}
```
### As Link
Use the `getHref` prop to create a pagination that navigates via links. This
will use the `LinkButton` component to create the links.
:::info
Edit the `LinkButton` component to point to the correct framework link component
if needed.
:::
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
export const PaginationAsLink = () => {
return (
`?page=${page}`}
>
)
}
```
### Attached
Here's an example of composing the pagination with the `Group` component to
attach the pagination items and triggers.
```tsx
'use client'
import { Group } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
export const PaginationAttached = () => {
return (
)
}
```
### Count Text
Pass `format=long` to the `PaginationPageText` component to show the count text
```tsx
'use client'
import { HStack } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
export const PaginationWithCountText = () => {
return (
)
}
```
### Data Driven
Here's an example of controlling the pagination state and using the state to
chunk the data.
```tsx
'use client'
import { useState } from 'react'
import { HStack, Stack, Text } from '@chakra-ui/react'
import { Pagination } from 'compositions/ui/pagination'
const pageSize = 5
const count = 50
const items = new Array(count)
.fill(0)
.map((_, index) => `Lorem ipsum dolor sit amet ${index + 1}`)
export const PaginationWithContent = () => {
const [page, setPage] = useState(1)
const startRange = (page - 1) * pageSize
const endRange = startRange + pageSize
const visibleItems = items.slice(startRange, endRange)
return (
{visibleItems.map((item) => (
{item}
))}
setPage(e.page)}
>
)
}
```
## Props
### Root
# Steps
```tsx
'use client'
import { Group, Button } from '@chakra-ui/react'
import { Steps } from 'compositions/ui/steps'
export const StepsBasic = () => {
return (
Step 1Step 2Step 3All steps are complete!
)
}
```
## Anatomy
```jsx
import { Steps } from '#components/ui/steps'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the steps component.
```tsx
'use client'
import { For, Group, Stack, Button } from '@chakra-ui/react'
import { Steps } from 'compositions/ui/steps'
export const StepsWithSizes = () => {
return (
{(size) => (
Step 1Step 2Step 3
All steps are complete!
)}
)
}
```
### Variants
Use the `variant` prop to change the appearance of the steps component.
```tsx
'use client'
import { For, Group, Stack, Button } from '@chakra-ui/react'
import { Steps } from 'compositions/ui/steps'
export const StepsWithVariants = () => {
return (
{(variant) => (
Step 1Step 2Step 3
All steps are complete!
)}
)
}
```
### Colors
Use the `colorPalette` prop to change the color scheme of the component.
```tsx
'use client'
import { For, Group, Stack, Button } from '@chakra-ui/react'
import { Steps } from 'compositions/ui/steps'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from '../lib/color-palettes'
export const StepsWithColors = () => {
return (
{(colorPalette) => (
Step 1Step 2Step 3
All steps are complete!
)}
)
}
```
### Vertical
Use the `orientation` prop to change the orientation of the steps component.
```tsx
'use client'
import { Group, Stack, Button } from '@chakra-ui/react'
import { Steps } from 'compositions/ui/steps'
export const StepsVertical = () => {
return (
Step 1Step 2Step 3All steps are complete!
)
}
```
### Icon
Pass the `icon` prop to the `StepsItem` component to display an icon.
```tsx
'use client'
import { Group, Button } from '@chakra-ui/react'
import { Steps } from 'compositions/ui/steps'
import { LuCalendar, LuUser, LuWallet } from 'react-icons/lu'
export const StepsWithIcon = () => {
return (
} />
} />
} />
Contact DetailsPaymentBook an AppointmentAll steps are complete!
)
}
```
### Description
Pass the `description` prop to the `StepsItem` component to display a
description.
```tsx
'use client'
import { Group, Button } from '@chakra-ui/react'
import { Steps } from 'compositions/ui/steps'
export const StepsWithDescription = () => {
return (
Step 1Step 2Step 3All steps are complete!
)
}
```
## Props
### Root
# Action Bar
```tsx
'use client';
import { useState } from 'react'
import { ActionBar } from 'compositions/ui/action-bar'
import { Button } from '@chakra-ui/react'
import { Checkbox } from 'compositions/ui/checkbox'
import { LuShare, LuTrash2 } from 'react-icons/lu'
export const ActionBarBasic = () => {
const [checked, setChecked] = useState(false)
return (
<>
setChecked(!!e.checked)}>
Show Action bar
2 selected
>
)
}
```
## Anatomy
The action bar is designed to be controlled by table or checkbox selections. It
provides a set of actions that can be performed on the selected items.
```jsx
import { ActionBar } from '#components/ui/action-bar'
```
```jsx
```
## Examples
### Close Trigger
Render the `ActionBarCloseTrigger` to close the action bar, and pass the
`onOpenChange` handler to control the visibility of the action bar.
> The `open` and `onOpenChange` props control the visibility of the action bar.
```tsx
'use client';
import { useState } from 'react'
import { ActionBar } from 'compositions/ui/action-bar'
import { Button } from '@chakra-ui/react'
import { Checkbox } from 'compositions/ui/checkbox'
import { LuShare, LuTrash2 } from 'react-icons/lu'
export const ActionBarWithCloseTrigger = () => {
const [checked, setChecked] = useState(false)
return (
<>
setChecked(!!e.checked)}
>
Show Action bar
setChecked(e.open)}
closeOnInteractOutside={false}
>
2 selected
>
)
}
```
### Within Dialog
Here's an example of composing the `ActionBar` and the `Dialog` to perform a
delete action on a set of selected items.
> Press the `Delete projects` button to open the dialog.
```tsx
'use client';
import { useState } from 'react'
import { ActionBar } from 'compositions/ui/action-bar'
import { Button } from '@chakra-ui/react'
import { Checkbox } from 'compositions/ui/checkbox'
import { Dialog } from 'compositions/ui/dialog'
import { LuSquarePlus, LuTrash2 } from 'react-icons/lu'
export const ActionBarWithDialog = () => {
const [checked, setChecked] = useState(false)
return (
<>
setChecked(!!e.checked)}>
Check to select projects
4 selectedDelete projects
Are you sure you want to delete 4 projects?
>
)
}
```
## Props
### Root
# Dialog
```tsx
'use client';
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
export const DialogBasic = () => {
return (
Dialog Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
)
}
```
## Anatomy
```jsx
import { Dialog } from '#components/ui/dialog'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to change the size of the dialog component.
```tsx
'use client'
import { For, HStack } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
export const DialogWithSizes = () => {
return (
{(size) => (
Dialog Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
do eiusmod tempor incididunt ut labore et dolore magna aliqua.
)}
)
}
```
### Cover
Use the `size="cover"` prop to make the dialog component cover the entire screen
while revealing a small portion of the page behind.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Dialog } from 'compositions/ui/dialog'
export const DialogWithCover = () => {
return (
Dialog Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
)
}
```
### Controlled
Use the `open` and `onOpenChange` prop to control the visibility of the dialog
component.
```tsx
'use client';
import { useState } from 'react'
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
import Lorem from 'react-lorem-ipsum'
export const DialogControlled = () => {
const [open, setOpen] = useState(false)
return (
setOpen(e.open)}>
Dialog Title
)
}
```
### Initial Focus
Use the `initialFocusEl` prop to set the initial focus of the dialog component.
```tsx
'use client'
import { useRef } from 'react'
import { Input, Stack } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
export const DialogWithInitialFocus = () => {
const ref = useRef(null)
return (
ref.current}>
Dialog Header
)
}
```
### Inside Scroll
Use the `scrollBehavior=inside` prop to change the scroll behavior of the dialog
when its content overflows.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Dialog } from 'compositions/ui/dialog'
import Lorem from 'react-lorem-ipsum'
export const DialogWithInsideScroll = () => {
return (
With Inside Scroll
)
}
```
### Outside Scroll
Use the `scrollBehavior=outside` prop to change the scroll behavior of the
dialog when its content overflows.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Dialog } from 'compositions/ui/dialog'
import Lorem from 'react-lorem-ipsum'
export const DialogWithOutsideScroll = () => {
return (
With Outside Scroll
)
}
```
### Motion Preset
Use the `motionPreset` prop to change the animation of the dialog component.
```tsx
'use client';
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
export const DialogWithMotionPreset = () => {
return (
Dialog Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
)
}
```
### Alert Dialog
Set the `role: "alertdialog"` prop to change the dialog component to an alert
dialog.
```tsx
'use client';
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
export const DialogWithRole = () => {
return (
Are you sure?
This action cannot be undone. This will permanently delete your
account and remove your data from our systems.
)
}
```
### With DataList
Here's an example of how to compose the dialog component with the `DataList`
component.
```tsx
'use client';
import { HStack, Textarea, VStack, Badge, Button, DataList } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
import { Dialog } from 'compositions/ui/dialog'
export const DialogWithDatalist = () => {
return (
Prepare Chakra V3StatusCompletedAssigned to
Eelco Wiersma
Due date12th August 2024
)
}
```
## Props
### Root
# Drawer
```tsx
'use client';
import { Button } from 'compositions/ui/button'
import { Drawer } from 'compositions/ui/drawer'
export const DrawerBasic = () => {
return (
Drawer Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
)
}
```
## Anatomy
```jsx
import { Drawer } from '#components/ui/drawer'
```
```jsx
```
## Examples
### Attached
Use the `attached` prop to render the drawer to attach to the edge of the viewport.
```tsx
'use client';
import { useState } from 'react'
import { Button } from 'compositions/ui/button'
import { Drawer } from 'compositions/ui/drawer'
export const DrawerAttached = () => {
const [open, setOpen] = useState(false)
return (
setOpen(e.open)} attached>
Drawer Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
)
}
```
### Sizes
Use the `size` prop to change the size of the drawer component.
```tsx
'use client'
import { For, HStack, Kbd } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Drawer } from 'compositions/ui/drawer'
export const DrawerWithSizes = () => {
return (
{(size) => (
Drawer Title
Press the esc key to close the drawer.
)}
)
}
```
### Offset
Pass the `offset` prop to the `DrawerContent` to change the offset of the drawer
component.
```tsx
'use client';
import { Button } from 'compositions/ui/button'
import { Drawer } from 'compositions/ui/drawer'
export const DrawerWithOffset = () => {
return (
Drawer Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
)
}
```
### Placement
Use the `placement` prop to change the placement of the drawer component.
```tsx
'use client'
import { For, HStack } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Drawer } from 'compositions/ui/drawer'
export const DrawerWithPlacement = () => {
return (
{(placement) => (
Drawer Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
)}
)
}
```
### Initial Focus
Use the `initialFocusEl` prop to set the initial focus of the drawer component.
```tsx
'use client'
import { useRef } from 'react'
import { Input, Stack } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Drawer } from 'compositions/ui/drawer'
export const DrawerWithInitialFocus = () => {
const ref = useRef(null)
return (
ref.current}>
Drawer Title
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
)
}
```
## Anatomy
```jsx
import { FloatingPanel } from '@chakra-ui/react'
```
```jsx
```
`FloatingPanel.ResizeTriggers` renders a `FloatingPanel.ResizeTrigger` for each
resize axis (`n`, `s`, `e`, `w`, `ne`, `nw`, `se`, `sw`). Pass the `axes` prop
to render only specific handles.
## Examples
### Controlled Open
Use the `open` and `onOpenChange` props to control the visibility of the panel.
```tsx
'use client'
import { useState } from 'react'
import {
Button,
FloatingPanel,
HStack,
IconButton,
Portal,
} from '@chakra-ui/react'
import { LuGripHorizontal, LuX } from 'react-icons/lu'
export const FloatingPanelControlledOpen = () => {
const [open, setOpen] = useState(false)
return (
setOpen(details.open)}
persistRect
defaultSize={{ width: 320, height: 200 }}
minSize={{ width: 320, height: 200 }}
>
Controlled Open
The open state is fully controlled externally. Closing via the
close button or the trigger both update the same state.
)
}
```
### Stages
Use the `stage` prop on `FloatingPanel.StageTrigger` to add buttons that
minimize, maximize and restore the panel. Here are the available stages:
- **`default`** — restore the panel to its normal size and position
- **`minimized`** — collapse the panel to a compact height (header only)
- **`maximized`** — expand the panel to fill the available boundary
```tsx
```
```tsx
'use client'
import {
Button,
FloatingPanel,
IconButton,
Portal,
Text,
} from '@chakra-ui/react'
import {
LuGripHorizontal,
LuMaximize2,
LuMinus,
LuSquare,
LuX,
} from 'react-icons/lu'
export const FloatingPanelStages = () => {
return (
Stages
Use the header controls to minimize, maximize, or restore the
panel.
)
}
```
### Context
Use `useFloatingPanelContext` inside the panel to call `minimize`, `maximize`,
`restore`, `setPosition`, and `setSize` programmatically.
```tsx
'use client'
import {
Button,
FloatingPanel,
HStack,
IconButton,
Portal,
Text,
useFloatingPanelContext,
} from '@chakra-ui/react'
import { LuGripHorizontal, LuX } from 'react-icons/lu'
const PanelActions = () => {
const api = useFloatingPanelContext()
return (
)
}
export const FloatingPanelContext = () => {
return (
Context
Control the panel from inside the body using{' '}
useFloatingPanelContext.
)
}
```
### Multiple
Render multiple `FloatingPanel.Root` instances to show several panels at once.
Each root manages its own open state, position, and size independently.
```tsx
'use client'
import {
Button,
FloatingPanel,
HStack,
IconButton,
Portal,
Text,
} from '@chakra-ui/react'
import { LuGripHorizontal, LuX } from 'react-icons/lu'
const panels = [
{
id: 'notes',
label: 'Notes',
title: 'Notes',
defaultPosition: { x: 80, y: 80 },
description: 'Jot down quick ideas and reminders.',
},
{
id: 'tasks',
label: 'Tasks',
title: 'Tasks',
defaultPosition: { x: 260, y: 120 },
description: 'Track items you want to finish today.',
},
{
id: 'chat',
label: 'Chat',
title: 'Chat',
defaultPosition: { x: 440, y: 160 },
description: 'Keep a lightweight conversation panel open.',
},
] as const
export const FloatingPanelMultiple = () => {
return (
{panels.map((panel) => (
{panel.title}{panel.description}
))}
)
}
```
### Resize Axes
Pass the `axes` prop to `FloatingPanel.ResizeTriggers` to limit which resize
handles are rendered.
```tsx
'use client'
import {
Button,
FloatingPanel,
IconButton,
Portal,
Text,
} from '@chakra-ui/react'
import { LuGripHorizontal, LuX } from 'react-icons/lu'
export const FloatingPanelResizeAxes = () => {
return (
Resize Axes
Only the south, east, and southeast handles are enabled.
)
}
```
### Min/Max
Use the `minSize` and `maxSize` props to constrain how small or large the panel
can be resized.
```tsx
'use client'
import {
Button,
FloatingPanel,
HStack,
IconButton,
Portal,
Span,
Stack,
Text,
} from '@chakra-ui/react'
import {
LuGripHorizontal,
LuMouse,
LuMoveHorizontal,
LuX,
} from 'react-icons/lu'
export const FloatingPanelMinMax = () => {
return (
Drag the resize handles — size is clamped between min and max.
Min/Max
Resize the panel with the resize handles. Size is constrained
between 280x160px (min) and 480x320px (max).
)
}
```
### Boundary
Use `getBoundaryEl` with `allowOverflow={false}` to confine the panel within a
specific container.
```tsx
'use client'
import { useRef } from 'react'
import {
Box,
Button,
FloatingPanel,
IconButton,
Portal,
Text,
} from '@chakra-ui/react'
import { LuGripHorizontal, LuX } from 'react-icons/lu'
export const FloatingPanelBoundary = () => {
const boundaryRef = useRef(null)
return (
Drag boundary
boundaryRef.current}
defaultSize={{ width: 280, height: 180 }}
minSize={{ width: 280, height: 180 }}
>
Bounded Panel
This panel cannot be dragged outside the dashed boundary box.
)
}
```
## Props
### Root
# Hover Card
```tsx
'use client';
import { Stack, Text } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
import { HoverCard } from 'compositions/ui/hover-card'
import { Link } from 'compositions/ui/link'
export const HoverCardBasic = () => {
return (
Follow{' '}
@saas_js
{' '}
for updates
Saas UI
@saas_js
Design system and React components for startups.
)
}
```
## Anatomy
```jsx
import { HoverCard } from '#components/ui/hover-card'
```
```jsx
```
## Examples
### Controlled
Use the `open` and `onOpenChange` to control the visibility of the hover card.
```tsx
'use client';
import { useState } from 'react'
import { Stack, Text } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
import { HoverCard } from 'compositions/ui/hover-card'
import { Link } from 'compositions/ui/link'
export const HoverCardControlled = () => {
const [open, setOpen] = useState(false)
return (
setOpen(e.open)}>
Follow{' '}
@saas_js
{' '}
for updates
Saas UI
@saas_js
Design system and React components for startups.
)
}
```
### Delays
Control the open and close delays using the `openDelay` and `closeDelay` props.
```tsx
'use client';
import { Stack, Text } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
import { HoverCard } from 'compositions/ui/hover-card'
import { Link } from 'compositions/ui/link'
export const HoverCardWithDelay = () => {
return (
Follow{' '}
@saas_js
{' '}
for updates
Saas UI
@saas_js
Design system and React components for startups.
)
}
```
### Placement
Use the `positioning.placement` prop to configure the underlying `floating-ui`
positioning logic.
```tsx
'use client';
import { Stack, Text } from '@chakra-ui/react'
import { Avatar } from 'compositions/ui/avatar'
import { HoverCard } from 'compositions/ui/hover-card'
import { Link } from 'compositions/ui/link'
export const HoverCardWithPlacement = () => {
return (
Follow{' '}
@saas_js
{' '}
for updates
Saas UI
@saas_js
Design system and React components for startups.
)
}
```
## Accessibility
HoverCard should be used solely for supplementary information that is not
essential for understanding the context.
It is inaccessible to screen readers and cannot be activated via keyboard, so
avoid placing crucial content within it.
## Props
# Menu
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
export const MenuBasic = () => {
return (
New Text FileNew File...New WindowOpen File...Export
)
}
```
## Anatomy
```jsx
import { Menu } from '#components/ui/menu'
```
```jsx
```
## Examples
### Sizes
Use the `size` prop to control the size of the menu. The default size is `md`.
```tsx
'use client';
import { HStack } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
export const MenuSizes = () => {
return (
OpenNew Text FileNew File...New WindowOpen File...ExportOpenNew Text FileNew File...New WindowOpen File...Export
)
}
```
### Command
Use the `MenuItemCommand` component to display a command in the menu.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
export const MenuWithCommand = () => {
return (
New Text File ⌘E
New File... ⌘N
New Window ⌘⇧N
Open File... ⌘O
Export ⌘S
)
}
```
### Context menu
Use the `MenuContextTrigger` component to create a context menu.
```tsx
'use client'
import { Center } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
export const MenuWithContextTrigger = () => {
return (
Right click here
New Text FileNew File...New WindowOpen File...Export
)
}
```
### Group
Use the `MenuItemGroup` component to group related menu items.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
export const MenuWithGroup = () => {
return (
BoldUnderlineLeftMiddleRight
)
}
```
### Submenu
Here's an example of how to create a submenu.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
export const MenuWithSubmenu = () => {
return (
New Text FileNew File...Open RecentPandaArk UIChakra v3Open File...Export
)
}
```
### Links
Pass the `asChild` prop to the `MenuItem` component to render a link.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
export const MenuWithLinks = () => {
return (
Naruto
One Piece
Attack on Titan
)
}
```
### With Radio
Here's an example of how to create a menu with radio.
```tsx
'use client';
import { useState } from 'react'
import { Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
import { HiSortAscending } from 'react-icons/hi'
export const MenuWithRadioItems = () => {
const [value, setValue] = useState('asc')
return (
setValue(e.value)}
>
}>
Ascending
}>
Descending
)
}
```
### With Checkbox
Here's an example of how to create a menu with checkbox.
```tsx
'use client';
import { useState } from 'react'
import { Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
import { HiFilter } from 'react-icons/hi'
export const MenuWithCheckboxItems = () => {
const [values, setValues] = useState({
open: false,
closed: false,
})
return (
setValues({ ...values, open: checked })}
value="open"
startElement={}
>
Open
setValues({ ...values, closed: checked })
}
value="closed"
startElement={}
>
Closed
)
}
```
### Icon and Command
Compose the menu to include icons and commands.
```tsx
'use client'
import { Box, Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
import { LuClipboardPaste, LuCopy, LuScissors } from 'react-icons/lu'
export const MenuWithIconAndCommand = () => {
return (
Cut⌘XCopy⌘CPaste⌘V
)
}
```
### Placement
Use the `positioning.placement` prop to control the placement of the menu.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
export const MenuWithPlacement = () => {
return (
New Text FileNew File...New WindowOpen File...Export
)
}
```
### Mixed Layout
Here's an example of how to create a mixed layout of menu items. In this layout,
the top horizontal menu includes common menu items.
```tsx
'use client'
import { Box, Group, Button } from '@chakra-ui/react'
import { Menu } from 'compositions/ui/menu'
import {
LuClipboard,
LuCopy,
LuFileSearch,
LuMessageSquare,
LuScissors,
LuShare,
} from 'react-icons/lu'
const horizontalMenuItems = [
{ label: 'Cut', value: 'cut', icon: },
{ label: 'Copy', value: 'copy', icon: },
{ label: 'Paste', value: 'paste', icon: },
]
const verticalMenuItems = [
{ label: 'Look Up', value: 'look-up', icon: },
{ label: 'Translate', value: 'translate', icon: },
{ label: 'Share', value: 'share', icon: },
]
export const MenuWithMixedLayout = () => {
return (
{horizontalMenuItems.map((item) => (
{item.icon}
{item.label}
))}
{verticalMenuItems.map((item) => (
{item.label}
{item.icon}
))}
)
}
```
## Props
### Root
### Item
# Modals
Install the modal manager with the CLI. The `dialog` and `drawer` templates are
installed automatically as dependencies.
```bash
pnpm dlx @saas-ui/cli@rc add modals
```
Add `ModalsProvider` near the root of your application.
```tsx
import { ModalsProvider } from '#components/ui/modals'
export function App({ children }: { children: React.ReactNode }) {
return {children}
}
```
Use `useModals` to open a modal, drawer, alert, or confirmation dialog.
```tsx
import { Button } from '@chakra-ui/react'
import { useModals } from '#components/ui/modals'
export function DeleteProject() {
const modals = useModals()
return (
)
}
```
## Custom modal types
Use `createModals` to create a manager with application-specific modal
components. The component props are inferred by `open`.
```tsx
import { Modal, type ModalProps, createModals } from '#components/ui/modals'
interface InviteModalProps extends Omit {
organizationId: string
}
function InviteModal({ organizationId, ...props }: InviteModalProps) {
return Invite a member to {organizationId}
}
export const { ModalsProvider, useModals } = createModals({
modals: { invite: InviteModal },
})
```
```tsx
const modals = useModals()
modals.open({ type: 'invite', organizationId: 'acme' })
```
# Overlay Manager
```tsx
'use client'
import { createOverlay } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
type DialogProps = {
title: string
description?: string
content?: React.ReactNode
}
const dialog = createOverlay((props) => {
const { title, description, content, ...rest } = props
return (
{title}
{description && (
{description}
)}
{content}
)
})
export const OverlayBasic = () => {
return (
<>
>
)
}
```
## Anatomy
```tsx
import { createOverlay } from '@chakra-ui/react'
```
`createOverlay` takes a component and returns a controller for it. Every prop
you pass to `open` is forwarded to the component, plus `open`, `onOpenChange`
and `onExitComplete`, which drive its lifecycle.
```tsx
import { createOverlay } from '@chakra-ui/react'
import { Dialog } from '#components/ui/dialog'
type DialogProps = {
title: string
description?: string
}
const dialog = createOverlay((props) => {
const { title, description, ...rest } = props
return (
{title}{description}
)
})
```
Render the `Viewport` once, somewhere in your tree, and open the overlay from
anywhere.
```tsx
```
```tsx
dialog.open('welcome', { title: 'Welcome' })
```
## Usage
The key things to note:
- Overlays are keyed by an `id`. Calling `open` with an id that already exists
updates that overlay instead of stacking a second one.
- The controller lives outside React, so you can trigger it from event handlers,
data mutations, or non-component code.
- `Viewport` must be mounted for anything to render. Mount it once per
controller.
- Because the overlay is rendered by the viewport rather than by the element
that triggered it, this pattern avoids the event bubbling and portal issues
you get when nesting a dialog inside a menu item.
## Examples
### Dialog
Open a dialog by calling `open` with an id and the props your component expects.
```tsx
'use client'
import { createOverlay } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
type DialogProps = {
title: string
description?: string
content?: React.ReactNode
}
const dialog = createOverlay((props) => {
const { title, description, content, ...rest } = props
return (
{title}
{description && (
{description}
)}
{content}
)
})
export const OverlayBasic = () => {
return (
<>
>
)
}
```
### Drawer
The same pattern works for any overlay component, including a drawer.
```tsx
'use client'
import { createOverlay } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Drawer } from 'compositions/ui/drawer'
type DrawerProps = {
title: string
description?: string
content?: React.ReactNode
placement?: Drawer.RootProps['placement']
}
const drawer = createOverlay((props) => {
const { title, description, content, ...rest } = props
return (
{title}
{description && (
{description}
)}
{content}
)
})
export const OverlayWithDrawer = () => {
return (
<>
>
)
}
```
### Update
Use `update` to change the props of an overlay that is already open.
```tsx
'use client'
import { Box, createOverlay } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
type DialogProps = {
title: string
description?: string
content?: React.ReactNode
}
const dialog = createOverlay((props) => {
const { title, description, content, ...rest } = props
return (
{title}
{description && (
{description}
)}
{content}
)
})
export const OverlayWithUpdate = () => {
return (
<>
>
)
}
```
### Return value
`open` returns a promise that resolves with the value passed to `close`, which
makes confirmation flows read top to bottom.
Use `waitForExit` to let the exit animation finish before opening the next
overlay.
```tsx
'use client'
import { createOverlay } from '@chakra-ui/react'
import { Button } from 'compositions/ui/button'
import { Dialog } from 'compositions/ui/dialog'
type DialogProps = {
title: string
description?: string
content?: React.ReactNode
}
type DialogResult = {
message: string
}
const dialog = createOverlay((props) => {
const { title, description, content, ...rest } = props
return (
{title}
{description && (
{description}
)}
{content}
)
})
export const OverlayWithReturnValue = () => {
return (
<>
>
)
}
```
### Closing from inside
To close an overlay from within the component itself, call the injected
`onOpenChange` prop. This is useful after a form submission or a successful
action.
```tsx
const dialog = createOverlay((props) => {
const { onOpenChange, ...rest } = props
const handleSubmit = async () => {
await save()
onOpenChange?.({ open: false })
}
return {/* ... */}
})
```
## API
### Injected props
These props are passed to your component by `createOverlay`.
| Prop | Type | Description |
| ---------------- | -------------------------------- | ------------------------------------------------- |
| `open` | `boolean` | Whether the overlay is currently open |
| `onOpenChange` | `(e: { open: boolean }) => void` | Fired when the overlay's open state changes |
| `onExitComplete` | `() => void` | Fired when the overlay's exit animation completes |
### Methods
| Method | Description |
| ------------------- | ---------------------------------------------------------------------- |
| `Viewport` | The component that renders all active overlays |
| `open(id, props)` | Opens an overlay. Returns a promise that resolves with the close value |
| `close(id, value)` | Closes the overlay with the given id, resolving `open` with `value` |
| `update(id, props)` | Updates the props of the overlay with the given id |
| `remove(id)` | Removes the overlay with the given id |
| `removeAll()` | Removes all overlays |
| `get(id)` | Returns the props of the overlay with the given id |
| `has(id)` | Returns whether an overlay with the given id exists |
| `getSnapshot()` | Returns the current list of overlays |
| `waitForExit(id)` | Resolves when the exit animation of the given overlay completes |
# Popover
```tsx
'use client'
import { Text, Button, Input } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverBasic = () => {
return (
Click me
Naruto Form
Naruto is a Japanese manga series written and illustrated by Masashi
Kishimoto.
)
}
```
## Anatomy
```jsx
import { Popover } from '#components/ui/popover'
```
```jsx
```
## Examples
### Controlled
Use the `open` and `onOpenChange` to control the visibility of the popover.
```tsx
'use client';
import { useState } from 'react'
import { Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverControlled = () => {
const [open, setOpen] = useState(false)
return (
setOpen(e.open)}>
Click me
This is a popover with the same width as the trigger button
)
}
```
### Sizes
Use the `size` prop to change the size of the popover component.
```tsx
import { For, Input, Stack, Text, Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverWithSizes = () => {
return (
{(size) => (
Click me
Naruto Form
Naruto is a Japanese manga series written and illustrated by
Masashi Kishimoto.
)}
)
}
```
### Lazy Mount
Use the `lazyMounted` and/or `unmountOnExit` prop to defer the mounting of the
popover content until it's opened.
```tsx
'use client'
import { Text, Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverLazyMounted = () => {
return (
Click me
Naruto Form
Naruto is a Japanese manga series written and illustrated by Masashi
Kishimoto.
)
}
```
### Placement
Use the `positioning.placement` prop to configure the underlying `floating-ui`
positioning logic.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverWithPlacement = () => {
return (
Click me
Some content
)
}
```
### Offset
Use the `positioning.offset` prop to adjust the position of the popover content.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverWithOffset = () => {
return (
Open
This is a popover with the same width as the trigger button
)
}
```
### Same Width
Use the `positioning.sameWidth` prop to make the popover content the same width
as the trigger.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverWithSameWidth = () => {
return (
Click me
This is a popover with the same width as the trigger button
)
}
```
### Nested Popover
When nesting overlay elements like popover, select, menu, inside of the popover,
set `portalled=false` on them.
Here's an example of a popover inside another popover.
```tsx
'use client'
import { Text, Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverNested = () => {
return (
Click me
Naruto is a Japanese manga series written and illustrated by Masashi
Kishimoto.
Open Nested Popover
Some nested popover content
)
}
```
### Initial Focus
Use the `initialFocusEl` prop to set the initial focus of the popover content.
```tsx
'use client'
import { useRef } from 'react'
import { Box, Group, Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverWithInitialFocus = () => {
const ref = useRef(null)
return (
ref.current}>
Click me
Manage Your Channels
This is a popover with the same width as the trigger button
Step 2 of 4
Prev
Next
)
}
```
### Form
Here's an example of a popover with a form inside.
```tsx
'use client'
import { Field, Input, Stack, Textarea, Button } from '@chakra-ui/react'
import { Popover } from 'compositions/ui/popover'
export const PopoverWithForm = () => {
return (
Click me
WidthHeightComments
)
}
```
## Props
### Root
# Toggle Tip
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { ToggleTip } from 'compositions/ui/toggle-tip'
import { LuInfo } from 'react-icons/lu'
export const ToggleTipBasic = () => {
return (
)
}
```
## Anatomy
```jsx
import { InfoTip } from '#components/ui/info-tip'
import { ToggleTip } from '#components/ui/toggle-tip'
```
```jsx
```
## Examples
### Info Tip
Use the `InfoTip` component to display an info tip. This component renders an
icon button with an info icon by default.
> Useful for landing pages to display additional information about a feature.
```tsx
'use client'
import { FormatByte, HStack, Text } from '@chakra-ui/react'
import { InfoTip } from 'compositions/ui/info-tip'
export const ToggleTipInfoTip = () => {
return (
File size: The file size for content.tsx is 1.45kb
)
}
```
## Props
### Root
# Tooltip
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Tooltip } from 'compositions/ui/tooltip'
export const TooltipBasic = () => {
return (
Hover me
)
}
```
## Anatomy
```jsx
import { Tooltip } from '#components/ui/tooltip'
```
```jsx
```
## Examples
### Arrow
Use the `showArrow` prop to show an arrow on the tooltip.
```tsx
'use client'
import { Button } from '@chakra-ui/react'
import { Tooltip } from 'compositions/ui/tooltip'
export const TooltipWithArrow = () => {
return (
Hover me
)
}
```
### Inverted
Use the `variant="inverted"` prop to invert the tooltip color.
```tsx
'use client'
import { Button } from '@chakra-ui/react'
import { Tooltip } from 'compositions/ui/tooltip'
export const TooltipWithInverted = () => {
return (
Hover me
)
}
```
### Placement
Use the `positioning.placement` prop to change the position of the tooltip.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Tooltip } from 'compositions/ui/tooltip'
export const TooltipWithPlacement = () => {
return (
Hover me
)
}
```
### Offset
Use the `positioning.offset` prop to change the offset of the tooltip.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Tooltip } from 'compositions/ui/tooltip'
export const TooltipWithOffset = () => {
return (
Hover me
)
}
```
### Delay
Use the `openDelay` and `closeDelay` prop to change the delay of the tooltip.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Tooltip } from 'compositions/ui/tooltip'
export const TooltipWithDelay = () => {
return (
Delay (open: 500ms, close: 100ms)
)
}
```
### Controlled
Use the `open` and `onOpenChange` prop to control the visibility of the tooltip.
```tsx
'use client';
import { useState } from 'react'
import { Button } from '@chakra-ui/react'
import { Tooltip } from 'compositions/ui/tooltip'
export const TooltipControlled = () => {
const [open, setOpen] = useState(false)
return (
setOpen(e.open)}
>
{open ? 'Hide' : 'Show'} tooltip
)
}
```
### Interactive
Use the `interactive` prop to keep the tooltip open when interacting with its
content.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Tooltip } from 'compositions/ui/tooltip'
export const TooltipWithInteractive = () => {
return (
Hover me
)
}
```
### Disabled
Use the `disabled` prop to disable the tooltip. When disabled, the tooltip will
not be shown.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { Tooltip } from 'compositions/ui/tooltip'
export const TooltipWithDisabled = () => {
return (
Hover me
)
}
```
## Props
### Root
# Blockquote
```tsx
'use client'
import { Blockquote } from 'compositions/ui/blockquote'
export const BlockquoteBasic = () => {
return (
If anyone thinks he is something when he is nothing, he deceives himself.
Each one should test his own actions. Then he can take pride in himself,
without comparing himself to anyone else.
)
}
```
## Usage
```jsx
import { Blockquote } from '#components/ui/blockquote'
export default function Example() {
return (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
)
}
```
## Examples
### With Cite
Use the `cite` prop to provide the source of the blockquote. This will be
displayed below the blockquote.
```tsx
'use client'
import { Blockquote } from 'compositions/ui/blockquote'
export const BlockquoteWithCite = () => {
return (
If anyone thinks he is something when he is nothing, he deceives himself.
Each one should test his own actions. Then he can take pride in himself,
without comparing himself to anyone else.
)
}
```
### Colors
Use the `colorPalette` prop to change the color of the blockquote.
```tsx
'use client'
import { Stack, Text } from '@chakra-ui/react'
import { Blockquote } from 'compositions/ui/blockquote'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from '../lib/color-palettes'
export const BlockquoteWithColors = () => {
return (
{["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"].map((colorPalette) => (
{colorPalette}
If anyone thinks he is something when he is nothing, he deceives
himself. Each one should test his own actions. Then he can take
pride in himself, without comparing himself to anyone else.
))}
)
}
```
### Variants
Use the `variant` prop to change the visual style of the blockquote. Values can
be either `subtle`, `solid`, `plain`.
```tsx
'use client'
import { Stack } from '@chakra-ui/react'
import { Blockquote } from 'compositions/ui/blockquote'
export const BlockquoteWithVariants = () => {
return (
If anyone thinks he is something when he is nothing, he deceives
himself. Each one should test his own actions. Then he can take pride in
himself, without comparing himself to anyone else.
If anyone thinks he is something when he is nothing, he deceives
himself. Each one should test his own actions. Then he can take pride in
himself, without comparing himself to anyone else.
)
}
```
### Icon
Use the `showIcon` prop to show an icon on the blockquote. The default icon is a
double quote.
```tsx
'use client';
import { Blockquote, BlockquoteIcon } from 'compositions/ui/blockquote'
import { Float } from '@chakra-ui/react'
export const BlockquoteWithIcon = () => {
return (
}
cite="Uzumaki Naruto"
>
If anyone thinks he is something when he is nothing, he deceives himself.
Each one should test his own actions. Then he can take pride in himself,
without comparing himself to anyone else.
)
}
```
### Custom Icon
Use the `icon` prop to change the icon of the blockquote.
```tsx
'use client'
import { Circle, Float } from '@chakra-ui/react'
import { Blockquote } from 'compositions/ui/blockquote'
import { LuQuote } from 'react-icons/lu'
export const BlockquoteWithCustomIcon = () => {
return (
}
>
If anyone thinks he is something when he is nothing, he deceives himself.
Each one should test his own actions. Then he can take pride in himself,
without comparing himself to anyone else.
)
}
```
## Props
### Root
# Code
```tsx
'use client'
import { Code } from '@chakra-ui/react'
export const CodeBasic = () => {
return {`console.log("Hello, world!")`}
}
```
## Usage
```js
import { Code } from "@chakra-ui/react"
```
```jsx
Hello world
```
## Examples
### Sizes
Use the `size` prop to change the size of the code component.
```tsx
'use client'
import { Code, Stack } from '@chakra-ui/react'
export const CodeWithSizes = () => {
return (
console.log()console.log()console.log()console.log()
)
}
```
### Variants
Use the `variant` prop to change the appearance of the code component.
```tsx
'use client'
import { Code, Stack } from '@chakra-ui/react'
export const CodeWithVariants = () => {
return (
console.log()console.log()console.log()console.log()
)
}
```
### Colors
Use the `colorPalette` prop to change the color scheme of the component.
```tsx
'use client'
import { Code, Stack, Text } from '@chakra-ui/react'
import { ["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"] } from '../lib/color-palettes'
export const CodeWithColors = () => {
return (
{["gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"].map((colorPalette) => (
{colorPalette}
{`console.log()`}
{`console.log()`}
{`console.log()`}
{`console.log()`}
))}
)
}
```
## Props
# Em
```tsx
import { Em, Text } from "@chakra-ui/react"
export const EmBasic = () => {
return (
The design system is a collection of UI elements
)
}
```
## Usage
```js
import { Em } from "@chakra-ui/react"
```
```jsx
The design system is a collection of UI elements
```
# Heading
```tsx
import { Heading } from "@chakra-ui/react"
export const HeadingBasic = () => {
return The quick brown fox jumps over the lazy dog
}
```
## Usage
```js
import { Heading } from '@chakra-ui/react'
```
```jsx
I'm a Heading
```
## Examples
### Sizes
Use the `size` prop to change the size of the heading component.
```tsx
import { Heading, Stack } from '@chakra-ui/react'
export const HeadingWithSizes = () => {
return (
Heading (sm)Heading (md)Heading (lg)Heading (xl)Heading (2xl)Heading (3xl)Heading (4xl)Heading (5xl)Heading (6xl)
)
}
```
```tsx
import { Heading, Stack } from '@chakra-ui/react'
export const HeadingWithMultiLine = () => {
return (
The quick brown fox
jumps over the lazy dog
The quick brown fox
jumps over the lazy dog
The quick brown fox
jumps over the lazy dog
The quick brown fox
jumps over the lazy dog
The quick brown fox
jumps over the lazy dog
The quick brown fox
jumps over the lazy dog
The quick brown fox
jumps over the lazy dog
The quick brown fox
jumps over the lazy dog
The quick brown fox
jumps over the lazy dog
)
}
```
### Highlight
Compose the `Heading` component with the `Highlight` component to highlight
text.
```tsx
import { Heading, Highlight, Stack, Text } from '@chakra-ui/react'
export const HeadingWithHighlight = () => {
return (
Build modern SaaS applications with Saas UI
Saas UI provides enterprise-ready components and patterns to help you
build professional applications faster than ever.
)
}
```
### As another element
Use the `as` prop to render the heading as another HTML element.
```tsx
import { Heading, Stack } from "@chakra-ui/react"
export const HeadingWithAsProp = () => {
return (
Level 1Level 2Level 3
)
}
```
### Weights
Use the `fontWeight` prop to change the weight of the heading component.
```tsx
import { Heading, Stack } from "@chakra-ui/react"
export const HeadingWithWeights = () => {
return (
NormalMediumSemiboldBold
)
}
```
### Composition
Use the `Heading` component to compose other components.
```tsx
import { Button, Heading, Stack, Text } from "@chakra-ui/react"
import { LuArrowRight } from "react-icons/lu"
export const HeadingWithComposition = () => {
return (
Modern payments for Stores
PayMe helps startups get paid by anyone, anywhere in the world
Create account
)
}
```
## Props
# Highlight
```tsx
import { Highlight } from "@chakra-ui/react"
export const HighlightBasic = () => {
return (
With the Highlight component, you can spotlight words.
)
}
```
## Usage
```jsx
import { Highlight } from "@chakra-ui/react"
```
```jsx
Hello World
```
## Examples
### Multiple
Pass an array of strings to the `query` prop to highlight multiple substrings.
```tsx
import { Heading, Highlight } from "@chakra-ui/react"
export const HighlightMultiple = () => {
return (
With the Highlight component, you can spotlight, emphasize and
accentuate words.
)
}
```
### Custom Style
Use the `styles` prop to customize the style of the highlighted text.
```tsx
import { Highlight } from "@chakra-ui/react"
export const HighlightWithCustomStyle = () => {
return (
With the Highlight component, you can spotlight words.
)
}
```
### Search Query
Use the highlight component to highlight search query results.
```tsx
import { Highlight, Stack, Text } from "@chakra-ui/react"
const query = "spot"
const results = ["Spotlight bulb", "Spot cleaner", "Spot ceiling"]
export const HighlightSearchQuery = () => {
return (
Search result for: spot
{results.map((item) => (
{item}
))}
)
}
```
## Props
# Kbd
```tsx
import { Kbd } from "@chakra-ui/react"
export const KbdBasic = () => {
return Shift + Tab
}
```
## Anatomy
```jsx
import { Kbd } from '@chakra-ui/react'
```
```jsx
F12
```
## Examples
### Combinations
Render `Kbd` to showcase key combinations
```tsx
import { HStack, Kbd } from "@chakra-ui/react"
export const KbdWithCombinations = () => {
return (
ctrl+shift+del
)
}
```
### Function Keys
Here's an example of using `Kbd` to showcase function keys
```tsx
import { HStack, Kbd } from "@chakra-ui/react"
export const KbdFunctionKeys = () => {
return (
⌘⌥⇧⌃
)
}
```
### Variants
Use the `variant` prop to change the appearance of the `Kbd` component
```tsx
import { HStack, Kbd } from "@chakra-ui/react"
export const KbdWithVariants = () => {
return (
Shift + TabShift + TabShift + TabShift + Tab
)
}
```
### Sizes
Use the `size` prop to change the size of the `Kbd` component
```tsx
import { HStack, Kbd } from "@chakra-ui/react"
export const KbdWithSizes = () => {
return (
Shift + TabShift + TabShift + Tab
)
}
```
### Within Text
Use `Kbd` within text to highlight key combinations
```tsx
import { Kbd, Text } from "@chakra-ui/react"
export const KbdWithinText = () => {
return (
Press F12 to open DevTools
)
}
```
## Props
# Link Overlay
```tsx
import { Heading, Link, LinkOverlay, Stack, Text } from "@chakra-ui/react"
export const LinkOverlayBasic = () => {
return (
Wanna try it out?
This entire area is a link. Click it to see the effect.
Click me
)
}
```
## Usage
The `LinkOverlay` component provides a semantic way to link an entire component
or card.
```jsx
import { LinkBox, LinkOverlay } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Article
Here's an example of using `LinkOverlay` to link an entire article.
```tsx
import {
Heading,
Link,
LinkBox,
LinkOverlay,
Span,
Text,
} from "@chakra-ui/react"
export const LinkOverlayArticle = () => {
return (
Chakra V3 Workshop
Catch up on whats been cooking at Chakra UI and explore some of the
popular community resources.
Inner Link
)
}
```
### Custom Link
Use the `asChild` prop to add support for custom Link component like `next/link`
or react router's `Link`
```jsx
import { LinkBox, LinkOverlay } from "@chakra-ui/react"
import NextLink from "next/link"
function Example() {
return (
Blog Post Title
Some blog post content
Some inner link
)
}
```
## Caveat
One of the side-effects of this technique is that the content can't be
"selectable" (i.e. with a pointing device), however, this seems to be pretty
uncommon in web design.
# Link
```tsx
import { Link } from "@chakra-ui/react"
export const LinkBasic = () => {
return Visit Chakra UI
}
```
## Usage
```jsx
import { Link } from "@chakra-ui/react"
```
```jsx
Click here
```
## Examples
### Variants
Use the `variant` prop to change the appearance of the `Link` component
```tsx
import { Link, Stack } from "@chakra-ui/react"
export const LinkWithVariants = () => {
return (
Link (Underline)
Link (Plain)
)
}
```
### Within Text
Use `Link` within a text to create a hyperlink
```tsx
import { Link, Text } from "@chakra-ui/react"
export const LinkWithinText = () => {
return (
Visit the{" "}
Chakra UI
{" "}
website
)
}
```
### External
Add an external link icon to the `Link` component
```tsx
import { Link } from "@chakra-ui/react"
import { LuExternalLink } from "react-icons/lu"
export const LinkWithExternal = () => {
return (
Visit Chakra UI
)
}
```
### Next.js
Compose `Link` with Next.js `Link` component using the `asChild` prop
```jsx
import { Link as ChakraLink } from "@chakra-ui/react"
import NextLink from "next/link"
const Demo = () => {
return (
Click here
)
}
```
## Props
# List
```tsx
import { List } from "@chakra-ui/react"
export const ListBasic = () => (
Lorem ipsum dolor sit amet, consectetur adipisicing elit
Assumenda, quia temporibus eveniet a libero incidunt suscipit
Quidem, ipsam illum quis sed voluptatum quae eum fugit earum
)
```
## Usage
```jsx
import { List } from '@chakra-ui/react'
```
```jsx
Item 1Item 2
```
## Examples
### Ordered
Pass the `as="ol"` prop to create an ordered list
```tsx
import { List } from "@chakra-ui/react"
export const ListOrdered = () => {
return (
Lorem ipsum dolor sit amet, consectetur adipisicing elit
Assumenda, quia temporibus eveniet a libero incidunt suscipit
Quidem, ipsam illum quis sed voluptatum quae eum fugit earum
)
}
```
### Icon
Use the `List.Indicator` component to add an icon to the list
```tsx
import { List } from "@chakra-ui/react"
import { LuCircleCheck, LuCircleDashed } from "react-icons/lu"
export const ListWithIcon = () => {
return (
Lorem ipsum dolor sit amet, consectetur adipisicing elit
Assumenda, quia temporibus eveniet a libero incidunt suscipit
Quidem, ipsam illum quis sed voluptatum quae eum fugit earum
)
}
```
### Nested
Here's an example of a nested list
```tsx
import { List } from "@chakra-ui/react"
export const ListNested = () => {
return (
First order itemFirst order item
First order item with list
Nested itemNested itemNested itemFirst order item
)
}
```
## Props
# Mark
```tsx
'use client'
import { Mark, Text } from '@chakra-ui/react'
export const MarkBasic = () => {
return (
The design system is a collection of UI
elements
)
}
```
## Usage
```js
import { Mark } from '@chakra-ui/react'
```
```jsx
The design system is a collection of UI elements
```
## Examples
### Variants
Use the `variant` prop to change the color of the mark.
## Props
# Prose
```tsx
import { Prose } from 'compositions/ui/prose'
// Used for syntax highlighting
const html = String.raw
const content = html`
Title Heading 1
Title Heading 2
Title Heading 3
Title Heading 4 testing
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi at dolor nec
ex rutrum semper. Praesent ultricies purus eget lectus tristique egestas ac
in lacus. Press Ctrl + C to copy.
Fusce placerat ipsum vel sollicitudin imperdiet. Morbi vulputate non diam at
consequat. Donec vitae sem eu arcu auctor scelerisque vel in turpis.
`
export const ProseBasic = () => {
return
}
```
## Anatomy
```jsx
import { Prose } from '#components/ui/prose'
```
```jsx
```
## Usage
`Prose` is a styled `div` that targets the raw HTML elements inside it —
headings, paragraphs, lists, tables, code, blockquotes and more. Use it when you
render content you don't control, like markdown, a rich text editor value, or
HTML from a CMS, where you can't attach Chakra components to each node.
Content is constrained to `65ch` by default for comfortable reading. Override it
with the `maxWidth` prop when you need a wider column.
## Examples
### Sizes
Use the `size` prop to change the base font size of the content. All spacing is
defined in `em`, so it scales with the size.
```tsx
import { For, Stack, Text } from '@chakra-ui/react'
import { Prose } from 'compositions/ui/prose'
export const ProseWithSizes = () => {
return (
{(size) => (
size: {size}
Title Heading 2
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi
at dolor nec ex rutrum semper. Praesent ultricies purus eget
lectus tristique egestas ac in lacus.
)}
)
}
```
### Blockquote
Blockquote elements are styled to match the design language of the
[Blockquote](/docs/components/blockquote) component.
```tsx
import { Prose } from 'compositions/ui/prose'
// Used for syntax highlighting
const html = String.raw
const content = html`
Blockquotes
This is a good looking blockquote!
And it can span into multiple lines:
Fusce placerat ipsum vel sollicitudin imperdiet. Morbi vulputate non diam at
consequat. Donec vitae sem eu arcu auctor scelerisque vel in turpis.
There's also strong, b and em support
as well.
`
export const ProseWithBlockquote = () => {
return
}
```
### List
List elements are styled to match the design language of the
[List](/docs/components/list) component.
```tsx
import { Prose } from 'compositions/ui/prose'
// Used for syntax highlighting
const html = String.raw
const content = html`
Lists
Let's look at some unordered lists. Things to buy:
Milk
Eggs
Bread
Saas UI Pro license
And some ordered lists. Things to do:
Pay the bills
Walk the dog
Take out trash
`
export const ProseWithList = () => {
return
}
```
### Table
Table elements are styled to match the design language of the
[Table](/docs/components/table) component.
```tsx
import { Prose } from 'compositions/ui/prose'
// Used for syntax highlighting
const html = String.raw
const content = html`
Tables
Plan
Seats
Price
Starter
3
$0
Team
25
$49
Enterprise
Unlimited
Custom
`
export const ProseWithTable = () => {
return
}
```
### React Markdown
`Prose` pairs well with a markdown renderer. Here's an example using
`react-markdown`.
```tsx
import { Prose } from 'compositions/ui/prose'
import Markdown from 'react-markdown'
const content = `
## Heading
Based on your Saas UI plan. So [click here](https://saas-ui.dev) to confirm your plan.
- first item
- second item
- third item
`
export const ProseWithReactMarkdown = () => {
return (
{content}
)
}
```
## Props
# Text
```tsx
import { Text } from "@chakra-ui/react"
export const TextBasic = () => {
return Sphinx of black quartz, judge my vow.
}
```
## Usage
```jsx
import { Text } from "@chakra-ui/react"
```
```jsx
This is the text component
```
## Examples
### Sizes
Use the `fontSize` or `textStyle` prop to change the size of the text.
```tsx
import { Stack, Text } from "@chakra-ui/react"
export const TextWithSizes = () => {
return (
ChakraChakraChakraChakraChakraChakraChakraChakraChakraChakraChakra
)
}
```
### Weights
Use the `fontWeight` prop to change the weight of the text.
```tsx
import { Stack, Text } from "@chakra-ui/react"
export const TextWithWeights = () => {
return (
Sphinx of black quartz, judge my vow.Sphinx of black quartz, judge my vow.Sphinx of black quartz, judge my vow.Sphinx of black quartz, judge my vow.Sphinx of black quartz, judge my vow.
)
}
```
### Truncation
Use the `truncate` prop to truncate the text after a single line.
```tsx
import { Flex, Text } from "@chakra-ui/react"
export const TextWithTruncate = () => {
return (
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
)
}
```
### Line Clamp
Use the `lineClamp` prop to truncate the text after a certain number of lines.
```tsx
import { Flex, Text } from "@chakra-ui/react"
export const TextWithLineClamp = () => {
return (
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat.
)
}
```
# Client Only
## Usage
```jsx
import { ClientOnly, Skeleton } from "@chakra-ui/react"
```
```jsx
}>
```
## Props
These props can be passed to the `ClientOnly` component.
# Environment Provider
We use
[Zag.js](https://zagjs.com/overview/composition#custom-window-environment)
internally, which relies on DOM query methods like `document.querySelectorAll`
and `document.getElementById`. In custom environments like iframes, Shadow DOM,
or Electron, these methods might not work as expected.
To handle this, Ark UI includes the `EnvironmentProvider`, allowing you to set
the appropriate root node or document, ensuring correct DOM queries.
## Usage
```jsx
import { EnvironmentProvider } from "@chakra-ui/react"
```
```jsx
{/* Your App */}
```
## Examples
### iframe
Here's an example that uses `react-frame-component` to set the
`EnvironmentProvider`'s value with the iframe environment.
```jsx
import { EnvironmentProvider } from "@chakra-ui/react"
import Frame, { FrameContextConsumer } from "react-frame-component"
export const Demo = () => (
{({ document }) => (
document}>
{/* Your App */}
)}
)
```
### Shadow DOM
Here's an example that uses `react-shadow` to set the `EnvironmentProvider`'s
value with Shadow DOM environment.
```jsx
import { EnvironmentProvider } from "@chakra-ui/react"
import { useRef } from "react"
import root from "react-shadow"
export const Demo = () => {
const portalRef = useRef()
return (
portalRef?.current?.shadowRoot ?? document}
>
{/* Your App */}
)
}
```
### Accessing Context
Use the `useEnvironmentContext` hook to access the `RootNode`, `Document`, and
`Window` context.
```jsx
import { useEnvironmentContext } from "@chakra-ui/react"
export const Demo = () => {
const { getRootNode } = useEnvironmentContext()
return
{JSON.stringify(getRootNode(), null, 2)}
}
```
## Props
# For
```tsx
import { For } from "@chakra-ui/react"
export const ForBasic = () => {
return (
{(item, index) =>
{item}
}
)
}
```
## Usage
The `For` component is used to render a list of items in a strongly typed
manner. It is similar to the `.map()`.
```jsx
import { For } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Object
Here's an example of using the `For` component to loop over an object.
```tsx
import { Box, For, Stack, Text } from "@chakra-ui/react"
export const ForWithObject = () => {
return (
{(item, index) => (
{item.name}Powers: {item.powers.join(", ")}
)}
)
}
```
### Fallback
Use the `fallback` prop to render a fallback component when the array is empty
or undefined.
```tsx
import { For, Stack, VStack } from '@chakra-ui/react'
import { LuBox } from 'react-icons/lu'
import { Box } from '../lib/decorative-box'
export const ForWithFallback = () => {
return (
No items to show
}
>
{(item, index) => (
{item}
)}
)
}
```
## Props
# Format Byte
```tsx
import { FormatByte, Text } from "@chakra-ui/react"
export const FormatByteBasic = () => {
return (
File size:
)
}
```
## Usage
```jsx
import { FormatByte } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Sizes
The format functions works for any size of bytes.
```tsx
import { FormatByte, Stack, Text } from "@chakra-ui/react"
export const FormatByteSizes = () => {
return (
)
}
```
### Format Bits
Use the `unit` prop to change the byte format to bits.
```tsx
import { FormatByte, Text } from "@chakra-ui/react"
export const FormatByteWithUnit = () => {
return (
File size:
)
}
```
### Locale
Wrap the `FormatByte` component within the `LocaleProvider` to change the
locale.
```tsx
import { FormatByte, HStack, LocaleProvider, Text } from "@chakra-ui/react"
export const FormatByteWithLocale = () => {
return (
de-DEzh-CN
)
}
```
### Unit Display
Use the `unitDisplay` prop to change the byte format to compact notation.
```tsx
import { FormatByte, Stack, Text } from "@chakra-ui/react"
export const FormatByteWithUnitDisplay = () => {
return (
)
}
```
## Props
# Format Date
## Usage
The date formatting logic is handled by the native `Intl.DateTimeFormat` API and
cached per locale and options combination to avoid performance issues.
```jsx
import { FormatDate } from '#components/ui/format-date'
```
```jsx
```
The `value` accepts a `Date`, a timestamp or a date string. Date-only strings
like `2026-03-18` are parsed as local calendar dates, so the rendered date never
shifts with the timezone of the visitor.
## Examples
### Options
`FormatDate` follows the same conventions as the Ark UI format components,
[FormatNumber](/docs/components/format-number) and
[FormatByte](/docs/components/format-byte). All `Intl.DateTimeFormat` options
are supported as props.
```jsx
```
### Locale
Wrap the `FormatDate` component within the `LocaleProvider` to change the
locale.
```jsx
```
### Data Table
The [DataTable](/docs/components/data-table) `DateCell` renders `FormatDate`
under the hood, and accepts the same `Intl.DateTimeFormat` options.
```tsx
columnHelper.accessor('renewal', {
header: 'Renewal',
cell: ({ cell }) => ,
})
```
## Props
The `FormatDate` component supports all `Intl.DateTimeFormat` options in
addition to the following props:
# Format Number
```tsx
import { FormatNumber, Text } from "@chakra-ui/react"
export const FormatNumberBasic = () => {
return (
)
}
```
## Usage
The number formatting logic is handled by the native `Intl.NumberFormat` API and
smartly cached to avoid performance issues when using the same locale and
options.
```jsx
import { FormatNumber } from "@chakra-ui/react"
```
```jsx
```
## Examples
### Percentage
Use the `style=percentage` prop to change the number format to percentage.
```tsx
import { FormatNumber, Text } from "@chakra-ui/react"
export const FormatNumberWithPercentage = () => {
return (
)
}
```
### Currency
Use the `style=currency` prop to change the number format to currency.
```tsx
import { FormatNumber, Text } from "@chakra-ui/react"
export const FormatNumberWithCurrency = () => {
return (
)
}
```
### Locale
Wrap the `FormatNumber` component within the `LocaleProvider` to change the
locale.
```tsx
import { FormatNumber, HStack, LocaleProvider, Text } from "@chakra-ui/react"
export const FormatNumberWithLocale = () => {
return (
de-DEzh-CN
)
}
```
### Unit
Use the `style=unit` prop to change the number format to unit.
```tsx
import { FormatNumber, Text } from "@chakra-ui/react"
export const FormatNumberWithUnit = () => {
return (
)
}
```
### Compact Notation
Use the `notation=compact` prop to change the number format to compact notation.
```tsx
import { FormatNumber, Text } from "@chakra-ui/react"
export const FormatNumberWithCompact = () => {
return (
)
}
```
## Props
The `FormatNumber` component supports all `Intl.NumberFormat` options in
addition to the following props:
# Locale Provider
## Usage
The `LocaleProvider` component sets the locale for your app, formatting dates,
numbers, and other locale-specific data.
> Most Chakra UI components that read the locale set by the `LocaleProvider`.
```jsx
import { LocaleProvider, useLocaleContext } from "@chakra-ui/react"
```
```jsx
{/* Your App */}
```
## Examples
### Setting Locale
Set the `locale` prop to the locale you want to use.
```jsx
```
### Reading Locale
```jsx
export const Usage = () => {
const { locale, dir } = useLocaleContext()
return
{JSON.stringify({ locale, dir }, null, 2)}
}
```
## Props
# Portal
## Usage
The `Portal` uses the `ReactDOM.createPortal` API to render an element at the
end of `document.body` or specific container.
```jsx
import { Portal } from '@chakra-ui/react'
```
```jsx
Portal content
```
## Examples
### Custom Container
Use the `containerRef` prop to render the portal in a custom container.
```jsx
import { Portal } from '@chakra-ui/react'
const Demo = () => {
const containerRef = React.useRef()
return (
<>
Portal content
>
)
}
```
### Disabled
Use the `disabled` prop to disable the portal. This will render the children in
the same DOM hierarchy.
```jsx
import { Portal } from '@chakra-ui/react'
const Demo = () => {
return (
Will render the content in place
)
}
```
## Server Rendering
During SSR, the `Portal` component directly renders its content. If you run into
any mismatch warnings, we recommended conditionally rendering the `Portal`
component only on the client-side.
## Props
# Presence
```tsx
'use client'
import {
Button,
Center,
Presence,
Stack,
useDisclosure,
} from '@chakra-ui/react'
export const PresenceFade = () => {
const { open, onToggle } = useDisclosure()
return (
Click Me
```
Think of `Presence` like the `AnimatePresence` component from Framer Motion,
except that it's built for CSS animations instead.
The key things to note:
- the `present` prop is a boolean that controls the presence state of the
component.
- the `_open` condition is used to style the open state.
- the `_closed` condition is used to style the closed state.
## Examples
### Fade
Setting the animation name to `fade-in` and `fade-out`, the component will
animate the entry and exit of the element.
```tsx
'use client'
import {
Button,
Center,
Presence,
Stack,
useDisclosure,
} from '@chakra-ui/react'
export const PresenceFade = () => {
const { open, onToggle } = useDisclosure()
return (
Click Me
Fade
)
}
```
### Scale Fade
Using the animation styles `scale-fade-in` and `scale-fade-out`, the component
will animate the entry and exit of the element.
```tsx
'use client'
import {
Button,
Center,
Presence,
Stack,
useDisclosure,
} from '@chakra-ui/react'
export const PresenceScaleFade = () => {
const { open, onToggle } = useDisclosure()
return (
Click Me
Scale Fade
)
}
```
### Slide Fade
Here's an example that uses the animation names `slide-from-bottom` and
`slide-to-bottom` to animate the entry and exit of the element.
```tsx
'use client'
import {
Button,
Center,
Presence,
Stack,
useDisclosure,
} from '@chakra-ui/react'
export const PresenceSlideFade = () => {
const { open, onToggle } = useDisclosure()
return (
Click Me
Slide Fade
)
}
```
### Lazy Mount
Use the `lazyMount` prop to delay the mount of the component until it's present.
```tsx
'use client'
import {
Alert,
Button,
Center,
Presence,
Stack,
useDisclosure,
} from '@chakra-ui/react'
export const PresenceLazyMount = () => {
const { open, onToggle } = useDisclosure()
return (
Check the DOM to see that the element not mounted initially
Click Me
Fade
)
}
```
### Unmount On Exit
Use the `unmountOnExit` prop to unmount the component when it's not present.
```tsx
'use client'
import {
Alert,
Button,
Center,
Presence,
Stack,
useDisclosure,
} from '@chakra-ui/react'
export const PresenceUnmountOnExit = () => {
const { open, onToggle } = useDisclosure()
return (
Check the DOM to see that the element is removed when not present.
Click Me
)
}
```
## Usage
The `Show` component renders its children when the `when` value is truthy,
otherwise it renders the `fallback` prop.
```jsx
import { Show } from "@chakra-ui/react"
```
```jsx
Content
```
## Examples
### Fallback
Use the `fallback` prop to render a fallback component when the array is empty
or undefined.
```tsx
"use client"
import { Button, Show, Stack, Text } from "@chakra-ui/react"
import { useState } from "react"
export const ShowWithFallback = () => {
const [count, setCount] = useState(0)
return (
setCount(count + 1)}>
Value: {count}
3}
fallback={Not there yet. Keep clicking...}
>
Congrats! I am here
)
}
```
### Render Prop
Use the `children` render prop to narrow the type of the `when` value and remove
`undefined` | `null`
```tsx
import { Show } from "@chakra-ui/react"
export const ShowWithRenderProp = () => {
const value: number | undefined = 10
return {(value) =>
Value: {value}
}
}
```
## Props
# Skip Nav
```tsx
import { Box, SkipNavContent, SkipNavLink, Stack, Text } from '@chakra-ui/react'
export const SkipNavBasic = () => {
return (
Skip to ContentNavigation
Repeated on every page, so keyboard users want to skip past it.
Main Content
Focus lands here when the skip link is activated.
)
}
```
## Anatomy
```jsx
import { SkipNavContent, SkipNavLink } from '@chakra-ui/react'
```
```jsx
Skip to content
```
## Usage
Per
[WCAG 2.4.1 (Bypass Blocks — Level A)](https://webaim.org/standards/wcag/checklist#sc2.4.1),
a page that repeats the same navigation across pages should offer a way to
bypass it. A screen reader user can jump between headings or landmarks, but a
sighted keyboard user cannot — they have to tab through every navigation link
before reaching the content.
The key things to note:
- `SkipNavLink` renders an `a` tag that is visually hidden until it receives
keyboard focus. Place it as high in the tree as possible, so it is the first
thing a user tabs to.
- `SkipNavContent` renders a `div` that acts as the target. It can be
self-closing or wrap your main content.
- Both components share a default `id`, so they link up without any
configuration.
## Examples
### Basic
Render `SkipNavLink` before your navigation and `SkipNavContent` right before
the main content. Tab into the example below to reveal the link.
```tsx
import { Box, SkipNavContent, SkipNavLink, Stack, Text } from '@chakra-ui/react'
export const SkipNavBasic = () => {
return (
Skip to ContentNavigation
Repeated on every page, so keyboard users want to skip past it.
Main Content
Focus lands here when the skip link is activated.
)
}
```
### Custom ID
Pass a custom `id` to change the anchor. The value must be set on **both**
components, otherwise the link has nothing to point at.
```tsx
import { Box, SkipNavContent, SkipNavLink, Stack, Text } from '@chakra-ui/react'
export const SkipNavCustomId = () => {
return (
Skip to Main ContentHeader & Navigation
Both components use the same main-content id.
Main Content
The id must match on both sides, otherwise the link does nothing.
)
}
```
### Wrapping the content
`SkipNavContent` can wrap the main content area instead of being self-closing.
This makes the whole region the focus target, which reads better in screen
readers.
```tsx
import {
Box,
Heading,
SkipNavContent,
SkipNavLink,
Stack,
Text,
} from '@chakra-ui/react'
export const SkipNavWithContent = () => {
return (
Skip to ContentSite Header
Home
About
Pricing
Contact
Welcome
Wrapping the main content makes the whole region the focus target,
so screen readers continue reading from here.
)
}
```
## Props
### Link
### Content
# Theme
```tsx
import { Button, Stack } from '@chakra-ui/react'
import { Theme } from 'compositions/ui/theme'
export const ThemeBasic = () => {
return (
Auto Button
Dark ButtonLight Button
)
}
```
## Anatomy
```jsx
import { Theme } from '#components/ui/theme'
```
```jsx
```
## Usage
`Theme` renders a `div` that scopes appearance to its subtree. It sets the
`chakra-theme` class together with the appearance class, and applies the native
`color-scheme` style so browser UI (scrollbars, form controls) follows along.
The key things to note:
- `appearance` forces `light` or `dark` for everything inside. Leave it off to
inherit the current color mode.
- `hasBackground` (default `true`) paints the `bg` and `fg` tokens. Set it to
`false` when the theme wraps an element that already has a background, like a
popover or dialog.
- `colorPalette` sets the default palette for all components in the subtree.
- The radius and scale props write CSS variables that the Saas UI semantic
tokens read, so they cascade to every component below.
## Examples
### Nested
Themes can be nested to apply different appearances to different parts of the
tree. This is useful for applying a global appearance and then overriding some
parts of it.
> Good to know: this uses native CSS selectors, so there is no extra React
> context involved.
```tsx
import { Box, Button } from '@chakra-ui/react'
import { Theme } from 'compositions/ui/theme'
export const ThemeNested = () => {
return (
Hello Normal Click me
Hello Dark Click me
Hello Light Click me
)
}
```
### Portalled
Portalled content, such as a popover or dialog, is rendered outside the tree and
therefore does not inherit a parent `Theme`. Wrap the portalled content itself
and set `hasBackground={false}` so it keeps the panel styles.
```tsx
import { Text, Button, Input } from '@chakra-ui/react'
import { Theme } from 'compositions/ui/theme'
import { Popover } from 'compositions/ui/popover'
export const ThemeWithPortalled = () => {
return (
Click me
Naruto Form
Naruto is a Japanese manga series written and illustrated by
Masashi Kishimoto.
Click me
)
}
```
### Radius
Use `controlRadius`, `panelRadius` and `indicatorRadius` to change the border
radius of controls, panels and indicators in a subtree. `scaleFactor` scales all
three at once.
```tsx
import { Button, Input, Stack, Text } from '@chakra-ui/react'
import { Theme } from 'compositions/ui/theme'
export const ThemeWithRadius = () => {
return (
controlRadius: 0
Subscribe
controlRadius: 2
Subscribe
)
}
```
### Page specific color mode
To lock an entire page to a specific color mode, wrap the page with `Theme`.
Combine it with `ColorModeProvider` when you also use the `useColorMode` hook.
```tsx
import { ColorModeProvider } from '#components/setup/color-mode/color-mode'
import { Theme } from '#components/ui/theme'
export const ForcedColorMode = ({ children }) => {
return (
{children}
)
}
```
## Props
# Visually Hidden
```tsx
import { Button, VisuallyHidden } from "@chakra-ui/react"
import { LuBell } from "react-icons/lu"
export const VisuallyHiddenBasic = () => {
return (
3 Notifications
)
}
```
## Usage
```jsx
import { VisuallyHidden } from "@chakra-ui/react"
```
```jsx
Hidden content
```
## Examples
### Input
Using the `asChild` prop, you can pass a child element to the `VisuallyHidden`
component.
```tsx
import { HStack, VisuallyHidden } from "@chakra-ui/react"
export const VisuallyHiddenWithInput = () => {
return (
The input is hidden
)
}
```
# Animation
We recommend using CSS animations to animate your Chakra UI components. This
approach is performant, straightforward and provides a lot of flexibility.
You can animate both the mounting and unmounting phases of your components with
better control.
## Enter animation
When a disclosure component (popover, dialog) is open, the `data-state`
attribute is set to `open`. This maps to `data-state=open` and can be styled
with `_open` pseudo prop.
```tsx
This is open
```
Here's an example that uses keyframes to create a fade-in animation:
```css
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
```
## Exit animation
When a disclosure component (popover, dialog) is closed, the `data-state`
attribute is set to `closed`. This maps to `data-state=closed` and can be styled
with `_closed` pseudo prop.
```tsx
This is closed
```
Here's an example that uses keyframes to create a fade-out animation:
```css
@keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
```
## Composing animations
Use the `animationName` prop to compose multiple animations together. This makes
it easy to create complex animations with multiple keyframes.
```tsx
This is a composed animation
```
# Color Mode
Saas UI uses [`next-themes`](https://github.com/pacocoursey/next-themes)
to add support for light and dark color mode.
## Composition
```tsx
import { ColorModeTrigger, DarkMode, LightMode, useColorMode, useColorModeValue } from '#components/setup/color-mode/color-mode'
```
## `useColorMode`
The `useColorMode` hook returns the current color mode and a function to toggle
the color mode.
```tsx
'use client';
import { Button } from '@chakra-ui/react'
import { useColorMode } from 'compositions/components/setup/color-mode/color-mode'
export const ColorModeBasic = () => {
const { toggleColorMode } = useColorMode()
return (
Toggle Mode
)
}
```
Calling `toggleColorMode` or `setColorMode` anywhere in your app tree toggles
the color mode from light or dark and vice versa.
## `useColorModeValue`
The `useColorModeValue` hook returns a value based on the current color mode.
Here's the signature:
```tsx
const result = useColorModeValue('', '')
```
The value returned will be the value of the light mode if the color mode is
`light`, and the value of the dark mode if the color mode is `dark`.
```tsx
'use client';
import { Box, Button, Stack } from '@chakra-ui/react'
import { useColorMode, useColorModeValue } from 'compositions/components/setup/color-mode/color-mode'
export const ColorModeValue = () => {
const { toggleColorMode } = useColorMode()
const bg = useColorModeValue('red.500', 'red.200')
const color = useColorModeValue('white', 'gray.800')
return (
This box's style will change based on the color mode.
Toggle Mode
)
}
```
## Hydration Mismatch
When using `useColorModeValue` or `useColorMode` in SSR, you may notice a
hydration mismatch when the page is mounted. This is because the color mode
value is computed on the server side.
To avoid this, use the `ClientOnly` component to wrap the component that uses
`useColorModeValue` and render a skeleton until mounted on the client side.
```tsx
'use client';
import { ClientOnly } from '@chakra-ui/react'
import { IconButton } from 'compositions/ui/icon-button'
import { Skeleton } from 'compositions/ui/skeleton'
import { useColorMode } from 'compositions/components/setup/color-mode/color-mode'
import { LuMoon, LuSun } from 'react-icons/lu'
export const ColorModeValueFallback = () => {
const { toggleColorMode, colorMode } = useColorMode()
return (
}>
{colorMode === 'light' ? : }
)
}
```
## Forced Color Mode
Using the `LightMode` and `DarkMode` components it's possible to force the color
mode for a specific component or a group of components.
```tsx
'use client';
import { Button, HStack } from '@chakra-ui/react'
import { DarkMode, LightMode, useColorMode } from 'compositions/components/setup/color-mode/color-mode'
export const ColorModeForced = () => {
const { toggleColorMode } = useColorMode()
return (
Light Mode Always
Dark Mode Always
Toggle Mode
)
}
```
# Composition
## The `as` Prop
Used to change the underlying HTML element that a React component renders. It
provides a straightforward way to change the underlying element while retaining
the component's functionality.
```jsx
Hello, world!
```
:::warning
**TypeScript:** The caveat with the `as` prop is that the types of the component
passed to the `as` prop must be compatible with the component's props. We do not
infer the underlying component's props from the `as` prop.
:::
## The `asChild` Prop
Used to compose a component's functionality onto its child element. This
approach, inspired by
[Radix UI](https://www.radix-ui.com/primitives/docs/utilities/slot), offers
maximum flexibility.
```jsx
Open
```
In this example, the `asChild` prop allows the `Button` to be used as the
trigger for the popover.
## Best Practices
To avoid common pitfalls when using the `as` and `asChild` props, there are a
few best practices to consider:
- **Forward Refs:** Ensure that the underlying component forwards the ref passed
to it properly.
- **Spread Props:** Ensure that the underlying component spreads the props
passed to it.
```jsx
const MyComponent = React.forwardRef((props, ref) => {
return
})
// with `as` prop
// with `asChild` prop
Click me
```
# Server Components
React Server Components is a new feature in React that allows you to build
components that render on the server and return UI to the client without hydration.
Client components are still server-rendered but hydrated on the client. Learn
more about
[Server component patterns](https://nextjs.org/docs/app/building-your-application/rendering/composition-patterns)
Chakra UI components are client components because they rely on `useState`,
`useRef` and `useState` which are not available in server components.
:::info
**TLDR:** By default, Chakra UI components can be used with React Server
Components without adding the 'use client' directive.
:::
## Usage
Here's an example of how to use Chakra UI components with React Server
Components in Next.js
```jsx [app/page.tsx]
import { Heading } from "@chakra-ui/react"
import fs from "node:fs"
export default async function Page() {
const content = fs.readFileSync("path/to/file.md", "utf-8")
return {content}
}
```
## Chakra Factory
When using the `chakra()` factory function, use the `use client` directive and
move the component to a dedicated file.
```jsx [blog-post.tsx]
"use client"
import { chakra } from "@chakra-ui/react"
export const BlogPost = chakra("div", {
base: {
color: "red",
},
variants: {
primary: {
true: { color: "blue" },
false: { color: "green" },
},
},
})
```
Then import the component in your page server component
```jsx [blogs/page.tsx]
import { BlogPost } from "./blog-post"
export default async function Page() {
const content = fs.readFileSync("path/to/file.md", "utf-8")
return {content}
}
```
## Hooks
When importing hooks from Chakra UI, use the `use client` directive
```jsx
"use client"
import { useBreakpointValue } from "@chakra-ui/react"
export function MyComponent() {
const value = useBreakpointValue({ base: "mobile", md: "desktop" })
return
{value}
}
```
## Render Props
When using render props, use the `use client` directive
```jsx
"use client"
import { ProgressContext } from "@chakra-ui/react"
export function MyComponent() {
return {({ value }) =>
{value}
}
}
```
# Testing
When writing tests with Vitest or Jest, use the following practices to get the
best results.
:::note
In general, we recommend using [Vitest](https://vitest.dev/) over
[Jest](https://jestjs.io/) but the setup are similar.
:::
## Setup
Before writing tests, ensure your project has the necessary dependencies:
```sh
npm install --save-dev vitest jsdom @testing-library/dom @testing-library/jest-dom @testing-library/react @testing-library/user-event
```
## Configuration
Create the `vite.config.ts` file to configure Vitest.
```ts
import { defineConfig } from "vitest/config"
export default defineConfig({
// ...
test: {
globals: true,
environment: "jsdom",
setupFiles: "./setup-test.ts",
},
})
```
Setting `globals: true` will automatically import the Vitest globals and removes
the need to import `expect`, `test`, `describe`, etc.
## Setup Test File
Create the `setup-test.ts` file to configure the testing environment and mock
unimplemented APIs.
Here's a common example for Chakra v3 projects:
```ts
import "@testing-library/jest-dom/vitest"
import { JSDOM } from "jsdom"
import ResizeObserver from "resize-observer-polyfill"
import { vi } from "vitest"
import "vitest-axe/extend-expect"
const { window } = new JSDOM()
// ResizeObserver mock
vi.stubGlobal("ResizeObserver", ResizeObserver)
window["ResizeObserver"] = ResizeObserver
// IntersectionObserver mock
const IntersectionObserverMock = vi.fn(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
takeRecords: vi.fn(),
unobserve: vi.fn(),
}))
vi.stubGlobal("IntersectionObserver", IntersectionObserverMock)
window["IntersectionObserver"] = IntersectionObserverMock
// Scroll Methods mock
window.Element.prototype.scrollTo = () => {}
window.Element.prototype.scrollIntoView = () => {}
// requestAnimationFrame mock
window.requestAnimationFrame = (cb) => setTimeout(cb, 1000 / 60)
// URL object mock
window.URL.createObjectURL = () => "https://i.pravatar.cc/300"
window.URL.revokeObjectURL = () => {}
// navigator mock
Object.defineProperty(window, "navigator", {
value: {
clipboard: {
writeText: vi.fn(),
},
},
})
// Override globalThis
Object.assign(global, { window, document: window.document })
```
## Custom Render
First, you need to create a custom render function to wrap your component in the
ChakraProvider.
```tsx title="test-utils/render.tsx"
// ./testing/render.tsx
import { Provider } from "@/components/ui/provider"
import { render as rtlRender } from "@testing-library/react"
export function render(ui: React.ReactNode) {
return rtlRender(<>{ui}>, {
wrapper: (props: React.PropsWithChildren) => (
{props.children}
),
})
}
```
## Testing Components
Now, you can use the `render` function to test your components.
```tsx title="testing/render.tsx"
import { Button } from "@chakra-ui/react"
import { render } from "./testing/render"
test("renders a button", () => {
render(Click me)
expect(screen.getByText("Click me")).toBeInTheDocument()
})
```
# Components
# Fields
The `form.Field` component can be used to create a field for any of these types.
- `text`, `email`, `url`, `phone`, `time`
- `number`
- `password`
- `textarea`
- `select`
- `checkbox`
- `radio`
If no `type` is provided, the field will default to `text`.
## Setup
Add `FieldsProvider` to your app to make the fields available to all forms.
```tsx
import { FieldsProvider, defaultFieldTypes } from '@saas-ui/forms'
function MyApp() {
return (
)
```
## Basic usage
```tsx
import { FormLayout, SubmitButton, useForm } from '@saas-ui/forms'
function MyForm() {
const form = useForm({
defaultValues: {
name: '',
email: '',
},
onSubmit: (values) => {
console.log(values)
},
})
return (
Save
)
}
```
## Built-in fields
### Text Field
```tsx
'use client'
import { Form, useAppForm } from 'compositions/components/forms'
export function FormText() {
const form = useAppForm({
defaultValues: {
name: '',
email: '',
phone: '',
time: '',
url: '',
},
onSubmit: ({ value }) => {
console.log(value)
},
})
return (
{(field) => }
{(field) => }
{(field) => (
)}
{(field) => }
{(field) => }
)
}
```
### Number Field
```tsx
'use client'
import { Form, useAppForm } from 'compositions/components/forms'
export function FormNumber() {
const form = useAppForm({
defaultValues: {
amount: '',
},
onSubmit: ({ value }) => {
console.log(value)
},
})
return (
{(field) => (
)}
{(field) => (
)}
{(field) => (
)}
)
}
```
### Password Field
```tsx
'use client'
import { Form, useAppForm } from 'compositions/components/forms'
export function FormPassword() {
const form = useAppForm({
defaultValues: {
password: '',
},
onSubmit: ({ value }) => {
console.log(value)
},
})
return (
{(field) => }
)
}
```
### Select Field
```tsx
'use client'
import { Form, useAppForm } from 'compositions/components/forms'
export function FormSelect() {
const form = useAppForm({
defaultValues: {
color: 'red',
},
onSubmit: ({ value }) => {
console.log(value)
},
})
return (
{(field) => (
)}
)
}
```
### Checkbox Field
```tsx
'use client'
import { Form, useAppForm } from 'compositions/components/forms'
import { Checkbox } from 'compositions/ui/checkbox'
export function FormCheckbox() {
const form = useAppForm({
defaultValues: {
terms: false,
},
onSubmit: ({ value }) => {
console.log(value)
},
})
return (
{(field) => (
field.handleChange(checked === true)
}
inputProps={{ onBlur: field.handleBlur }}
>
Accept terms and conditions
)}
)
}
```
### Radio Field
```tsx
'use client'
import { Fieldset, HStack, RadioGroup } from '@chakra-ui/react'
import { Form, useAppForm } from 'compositions/components/forms'
import { Radio } from 'compositions/ui/radio'
export function FormRadio() {
const form = useAppForm({
defaultValues: {
color: 'red',
},
onSubmit: ({ value }) => {
console.log(value)
},
})
return (
{(field) => (
Color field.handleChange(value ?? '')}
onBlur={field.handleBlur}
>
RedGreenBlue
)}
)
}
```
### Switch Field
```tsx
'use client'
import { Form, useAppForm } from 'compositions/components/forms'
export function FormSwitch() {
const form = useAppForm({
defaultValues: {
notifications: false,
},
onSubmit: ({ value }) => {
console.log(value)
},
})
return (
{(field) => }
)
}
```
## Custom fields
Since `useForm` is built on top of `react-hook-form`, you can use any of the
[react-hook-form](https://react-hook-form.com/docs/useform) API to create custom
fields.
This is useful when you need to create a one off field that is not supported by
the built-in fields.
```tsx
import { Controller, SubmitButton, useForm } from '@saas-ui/forms'
function MyForm() {
const form = useForm({
defaultValues: {
name: '',
},
onSubmit: (values) => {
console.log(values)
},
})
return (
}
/>
Submit
)
}
```
To create a re-usable custom field, you can use the `createField` function to
define a field type that can be passed to the `useForm` hook.
```tsx
import { createField, useForm } from '@saas-ui/forms'
interface InputFieldProps extends InputProps {
type?: string
startElement?: React.ReactNode
endElement?: React.ReactNode
}
const InputField = createField(
({ type = 'text', startElement, endElement, size, ...rest }, ref) => {
return (
)
},
)
```
The `createField` function takes two arguments:
- The component to render
- An options object
The options object can be used to configure the field type.
The `createField` function returns a React component that can be used to render
the field.
```tsx
// App.tsx
import { createField, FieldsProvider, defaultFieldTypes } from '@saas-ui/forms'
const CustomInputField = createField(
(props, ref) => {
return
},
)
export default function App() {
return (
)
}
```
```tsx
// Form.tsx
import { useForm } from '@saas-ui/forms'
function MyForm() {
const form = useForm({
defaultValues: {
name: '',
},
onSubmit: (values) => {
console.log(values)
},
})
return (
Submit
)
}
```
# Forms
Saas UI provides a set of form utilities, hooks and components that are built on
top of [react-hook-form](https://react-hook-form.com/), [Zod](https://zod.dev/)
and [Chakra UI](https://chakra-ui.com/).
## Installation
Saas UI forms ships as a separate NPM package.
```bash
npm install @saas-ui/forms
```
## Usage
```tsx
import { SubmitButton, useForm } from '@saas-ui/forms'
function MyForm() {
const form = useForm({
defaultValues: {
name: '',
},
onSubmit: (values) => {
console.log(values)
},
})
return (
Submit
)
}
```
### Schema validation
The `useForm` hook supports any Standard Schema supported schema library. We
recommend using [Zod](https://zod.dev/) for form validation, but you can use
other libraries like [Valibot](https://valibot.dev/) or
[ArkType](https://arktype.io).
The default values and field names are inferred from the schema for Type-Safe
forms.
When using a schema the form will not submit until the schema is valid, and
validation errors will be displayed along with the form fields.
Use the `onInvalid` callback to handle validation errors, for example to trigger
a toast notification.
```tsx
import { SubmitButton, useForm } from '@saas-ui/forms'
import { toast } from '#components/ui/toaster'
import { z } from 'zod'
const schema = z.object({
name: z.string().min(1),
})
function MyForm() {
const form = useForm({
schema,
defaultValues: {
name: '',
},
onInvalid: (errors) => {
console.log(errors)
toast.error({
title: 'Invalid form',
description: 'Please check the form for errors',
})
},
onSubmit: (values) => {
console.log(values)
},
})
return (
Submit
)
}
```
### Built in fields
Saas UI provides a set of built-in field types that can be used to create
beautiful forms rapidly with minimal code.
Support for the following field types is built in:
- `text`
- `email`
- `number`
- `email`
- `password`
- `textarea`
- `select`
- `checkbox`
- `radio`
The `form.Field` component can be used to create a field for any of these types.
See the [Fields](/docs/forms/fields) page for more details.
### Submit button
The `SubmitButton` component is a simple button that will submit the form when
clicked.
If the `onSubmit` callback is async, the `SubmitButton` will show a loading
state when the form is submitted.
To disable the button when the form is untouched or invalid, use the
`disableIfUntouched` and `disableIfInvalid` props.
```tsx
import { SubmitButton, useForm } from '@saas-ui/forms'
function MyForm() {
const form = useForm({
onSubmit: async (values) => {
console.log(values)
},
})
return (
Submit
)
}
```
### Conditional fields
The `DisplayIf` component can be used to conditionally render fields based on
the values of other fields.
```tsx
import { SubmitButton, useForm } from '@saas-ui/forms'
function MyForm() {
const form = useForm({
defaultValues: {
name: '',
age: '',
},
onSubmit: (values) => {
console.log(values)
},
})
return (
value !== 'Balrog'}>
Submit
)
}
```
# CLI
The Saas UI CLI initializes Chakra UI in your project and installs editable
compositions, blocks and custom components from the Saas UI registry.
## Installation
You can run the CLI directly with `npx`, without adding it to your project:
```bash
npx @saas-ui/cli@rc init
```
Or install it as a dev dependency:
:::code-group
```bash [npm]
npm i -D @saas-ui/cli@rc
```
```bash [yarn]
yarn add -D @saas-ui/cli@rc
```
```bash [pnpm]
pnpm add -D @saas-ui/cli@rc
```
```bash [bun]
bun add -d @saas-ui/cli@rc
```
:::
The CLI installs two executables, `saas-ui` and the shorter `sui`. Both run the
same program. The examples on this page use `npx @saas-ui/cli@rc`, replace it
with `saas-ui` if you installed the CLI globally.
> The minimum node version required is Node 22.x
## Usage
```bash
Usage: saas-ui [options] [command]
Saas UI command line
Options:
-v, --version Output the version number
-h, --help Display help for a command
Commands:
login Log in with your Saas UI account
init Initialize a project
add Add components to your project
diff Compare installed registry items with local files and the registry
list List all available components
update Update installed registry items from the current registry
migrate Migrate legacy Saas UI projects
```
Flags accept both kebab-case and camelCase, `--dry-run` and `--dryRun` are
equivalent.
## login
Log in with your Saas UI account. This is required to install Pro blocks and
templates.
```bash
npx @saas-ui/cli@rc login
```
The command starts a temporary local server and opens your browser. After
authenticating, the token is stored in your user config so subsequent `add`
commands can resolve private registry items. The command takes no options.
## init
Initialize a project. Installs `@chakra-ui/react`, `@emotion/react` and
`@saas-ui/chakra-preset`, writes `components.json` and installs the matching
local provider setup.
```bash
npx @saas-ui/cli@rc init
```
Any positional arguments are treated as extra starter components to install.
| Option | Description | Default |
| --------------------------- | --------------------------------------------------------- | -------------------- |
| `--yes` | Skip the confirmation prompt. | `false` |
| `--defaults` | Skip prompts and use the default configuration. | `false` |
| `--force` | Overwrite an existing, conflicting configuration. | `false` |
| `--color-mode ` | Install color mode support. `on` also adds `next-themes`. | Prompted |
| `--starter` | Install the documented starter set (currently `sidebar`). | `false` |
| `--style