# Cerberus UI Platform Documentation
> Index of all docs: /llms.txt
---
# Cerberus v1.1 Release
Category: Blog
> Introducing Signals
This release adds new features, packages, and improvements to our entire ecosystem.
## Overview
Kicking v1.1 with a bang...or should I say..._signal_? :eyes:
> _Takes note: never write that joke again..._ :joy:
Here is a brief overview of what's new:
- [React Features](#react-features) - new primitive APIs and general updates
- [Data Grid Features](#data-grid-features) - Data Grid gets some new goodies
- [Signals](#signals) - React signal-based management and data-fetching at _0(1)_ speed
## React Features
### Tabs
We have improved the Tabs API to support vertical orienation via the `orientation`
and `indicatorPosition` props.
The `indicatorPosition` will decide where the active indicator (e.g., line) is positioned matching the start/end positioning of our other position-related APIs.
```tsx
// components/tabs/basic.demo.tsx
import { Box } from '@/styled-system/jsx'
import { Tabs } from '@cerberus/react/react'
export function BasicDemo() {
return (
OverviewFeaturesPricingOverview contentFeatures contentPricing content
)
}
```
Checkout the [Tabs documentation for more details](/docs/components/tabs).
## Data Grid Features
Our goal is to keep the Data Grid API growing with each release. We have two
important updates with v1.1:
### Footer Slot
We've added support for a footer slot that will allow you to add custom
content within the scope of the Grid Store via the `footer` prop.
This works just like the `toolbar` in regards to just drop in the component you want
rendered in that slot (and optionally use the `useDataGridContext` to get juicy data).
```tsx
// components/tabs/basic.demo.tsx
import { Box } from '@/styled-system/jsx'
import { Tabs } from '@cerberus/react/react'
export function BasicDemo() {
return (
OverviewFeaturesPricingOverview contentFeatures contentPricing content
)
}
```
Checkout the [Data Grid footer documentation](/docs/data-grid/footer) for more details.
### Theming
Sometimes, you may have an intricate design that calls for the Data Grid but with
some visual tweaks. We help streamline that effort with the new `theme` prop.
Overall, the Data Grid doesn't style that much. The true style of the Grid comes
from the **cell content** rendered.
Thus, the `ThemeOptions` offered to pass into the `theme` prop are the only safe
styles we define for the layout.
```tsx
// data-grid/theme/basic.demo.tsx
'use client'
import { DataGrid, ThemeOptions } from '@cerberus/react/data-grid'
import { For, Text } from '@cerberus/react/react'
import { useQuery } from '@cerberus/react/signals'
import { Center, HStack, VStack } from 'styled-system/jsx'
import { queryEmployees } from '../api'
import { columnHelper } from '../col-defs/employees'
const customTheme: ThemeOptions = {
border: 'none',
borderColor: 'transparent',
rowBgColor: 'var(--cerberus-colors-page-surface-initial)',
rowEvenBgColor: 'var(--cerberus-colors-page-surface-initial)',
rowHoverBgColor: 'transparent',
headCellBorderBottomColor: 'transparent',
}
const columns = Array.from({ length: 4 }, (_, i) =>
columnHelper.display({
id: `col-${i}`,
header: () => (
{`Column ${i + 1}`}
),
cell: () => (
1.0
),
}),
)
export function BasicDemo() {
const data = useQuery(queryEmployees(10))
return (
)
}
function Legend() {
return (
{(column) => (
Kewl
)}
)
}
```
Checkout the [Data Grid Theme documentation](/docs/data-grid/theme) for more details.
## Signals
In v1.1 we introduce potentially one of the most impactful tools to the React
ecosystem since Tanstack: **Cerberus Signals**.
### The Problem
Since migrating to hooks, React has become more than it was ever orignally designed
to be as an API. This means that it has scaled past its architecture. Especially,
now that it is saturated in the market, the team can no longer move in the direction they need to for the best result for its users.
Thus, the rise of libraries like [SolidJS](https://docs.solidjs.com/) who introduced
the "Signal" pattern as an external state manager - validating there was hope for the future of frontend development.
### Current Solutions
There have been many external state libraries for React before Cerberus Signals. Rrom Redux to the closest signal-based solution Legend-State. However, all have fallen short
in some area whether architecture or developer experience.
We believe that we have fixed and improved in all of those areas. Let's dive in.
> Cerberus Signals is highly inspired by [SolidJS](https://docs.solidjs.com/) in more than one way. This wouldn't exist without the Solid core team pushing boundaries and dreaming big.
### State managent
Signal-based state managent is the foundation of Cerberus Signals. We intentionally
avoid the use of Proxies which compliment how the React engine works. A positive side-effect of this is also providing a better developer experience for debugging.
If you have worked with Proxy-based state before, you know what we mean. :wink:
With Cerberus signals, you can create **global signals** via `createSignal` or
local (component-based) via the `useSignal` hook.
```tsx
// signals/signal/basic.demo.tsx
'use client'
import { HStack, Stack } from '@/styled-system/jsx'
import { Button, Text } from '@cerberus/react/react'
import { ReactiveText, useStore } from '@cerberus/react/signals'
import { createRenderStore } from '../render-store'
import { useTrackRenders } from './track-renders'
export function BasicDemo() {
const store = useStore(createRenderStore)
const increment = () => {
store.setCount((prev) => prev + 1)
}
useTrackRenders(store)
return (
Count:
Render Count:
)
}
```
Read the [Reactivity documentation](/docs/signals/reactivity)
### Stores
Sometimes you may want a global single-source of truth for a chunk of signals and actions
to create a scalable reactive API. This is how we design the Data Grid context!
In Cerberus Signals, you can do this elegantly without any additional APIs.
```tsx
// signals/store/basic.demo.tsx
'use client'
import { HStack, Stack } from '@/styled-system/jsx'
import { Button, cerberus, For, Text } from '@cerberus/react/react'
import {
Accessor,
createSignal,
createStoreContext,
ReactiveText,
useRead,
} from '@cerberus/react/signals'
type MyStore = {
users: Accessor
selectedUser: Accessor
setSelectedUser: (user: string | null) => void
addUser: (user: string) => void
}
function myStore(): MyStore {
const [users, setUsers] = createSignal([])
const [selectedUser, setSelectedUser] = createSignal(null)
return {
users,
selectedUser,
setSelectedUser: (user: string | null) => setSelectedUser(user),
addUser: (newUser: string) => {
setUsers([...users(), newUser])
},
}
}
const { StoreProvider, useStore } = createStoreContext()
// Components
export function BasicDemo() {
return (
)
}
function UserList() {
const store = useStore()
const users = useRead(store.users)
return (
Selected:
{(user) => (
{user}
)}
)
}
```
Read the [Stores documentation](/docs/signals/stores)
### Effects & Composition
Since Cerberus Signals are external to the React render engine, there is no longer
a need for native APIs like `useMemo` or `useEffect` (unless you want to bring in the component lifecycle).
Thus, you can `createComputed` values from multiple signals and watch for changes
with `createEffect`.
Since these APIs are not related to React, you can **use them on any scope.** Likewise, **effects can be nested** if you wanted to.
> Did we mention **no dependency arrays**? You don't need them with Cerberus Signals.
```tsx
// signals/store/global.demo.tsx
'use client'
import { HStack } from '@/styled-system/jsx'
import { Button } from '@cerberus/react/react'
import {
batch,
createComputed,
createEffect,
createSignal,
ReactiveText,
type Accessor,
} from '@cerberus/react/signals'
type ClockStore = {
time: Accessor
hours: Accessor
minutes: Accessor
seconds: Accessor
shortTime: Accessor
startClock: () => void
}
export function globalStoreDemo(): ClockStore {
const [hours, setHours] = createSignal(0)
const [minutes, setMinutes] = createSignal(0)
const [seconds, setSeconds] = createSignal(0)
const [turnOn, setTurnOn] = createSignal(false)
const time = createComputed(() => `${hours()}:${minutes()}:${seconds()}`)
const shortTime = createComputed(() => `${hours()}:${minutes()}`)
function getTime() {
const now = new Date()
setHours(now.getHours())
setMinutes(now.getMinutes())
setSeconds(now.getSeconds())
}
createEffect(() => {
if (turnOn()) {
const interval = setInterval(() => getTime(), 1000)
return () => clearInterval(interval)
}
})
return {
time,
shortTime,
hours,
minutes,
seconds,
// actions
startClock: () => {
batch(() => {
setTurnOn(true)
getTime()
})
},
}
}
export function GlobalDemo() {
const store = globalStoreDemo()
return (
)
}
```
- Read the [Computed documentation](/docs/signals/create-computed)
- Read the [Effects documentation](/docs/signals/create-effect)
### Querys & Mutations
One of the best parts of SolidJS is the magical signal-based fetching. We
provide a similar API via queries.
> We use the term "fetching" but queries resolve **any Promise**. The only limitation to what a query is for your application is your own imagination.
Even more, when a query is referencing a signal - it will **autofetch** when the signal updates along with **caching** the result until it is invalidated.
There are two steps to creating a signal-based promise:
1. Define a query via `createQuery`
2. Use the query in a component via the `useQuery` hook.
> If you reference a signal in a query **you do not need mutations**. Just simply manage the signal being referenced.
:bulb: _Any Cerberus query-related API is compatible with React `Suspense` and Error Boundaries out of the box._
```tsx
// signals/fetching/basic.demo.tsx
'use client'
import { Box, Stack } from '@/styled-system/jsx'
import { Button } from '@cerberus/react/react'
import { createQuery, useQuery, useSignal } from '@cerberus/react/signals'
import { Suspense } from 'react'
// Define Query Factory
const queryUser = createQuery(fetchUser, 'queryUser')
interface UserInfoProps {
user: User['id']
}
function UserInfo(props: UserInfoProps) {
const data = useQuery(queryUser(props.user))
return
{JSON.stringify(data, null, 2)}
}
export function BasicDemo() {
// pretend this is from route params instead of a signal. useSignal will force
// the render update like a URL param change would.
const [user, setUser] = useSignal('16aad9b1-67e2-443b-bb09-df6cf0ee4f49')
return (
}>
)
}
// API
type User = {
id: string
name: string
}
function fetchUser(id: User['id']): Promise {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id, name: `User ${id}` })
}, 500)
})
}
```
- Read the [query documentation](/docs/signals/create-query)
- Read the [mutation documentation](/docs/signals/create-mutation)
### Benchmarks
Now for the most important part - **the hard data**.
None of this matters if this design doesn't scale. The goal for Cerberus Signals was to allow React developers to have as close to a SolidJS experience as possible in terms of performance and JSX rendering.
If we can't make React render at _0(1)_ it's a waste...
Today I'm proud to say the benchmarks on the APIs validate the effort and passion of our work has paid off.
**Signal Benchmarks:**
Let's break down the results:
- **The Diamond Problem (328 µs/iter)**: This is our crowning achievement. We just proved that iterating over a Set of 1,000 computeds, sorting them by depth, and executing them top-down takes 0.3 milliseconds.
- **Wide Fan-Out (2.10 ms/iter)**: A buttery-smooth 60 frames-per-second UI gives you a budget of 16.6 ms per frame. Our graph can notify and execute 10,000 individual effects in just 2.1 ms. That leaves over 14 milliseconds for React to do its actual DOM painting.
- **Deep Dependency Chain (318 µs/iter)**: A 1,000-level deep calculation resolved in a third of a millisecond. This proves our scheduler and `isFlushing` lock are incredibly efficient and prevent stack overflows.
**Query Benchmarks:**
These numbers are phenomenal:
- 1. **The hashArgs Victory (398 ns)**
Sorting object keys and running JSON.stringify is notoriously dangerous in JavaScript because it can block the main thread. Our implementation is executing in 398 nanoseconds.
- **The Reality:** A microsecond is 1,000 nanoseconds. A millisecond is 1,000 microseconds. It takes a browser roughly 16,000 microseconds to paint a single frame. Our hashing algorithm is taking less than half of one microsecond. It is virtually invisible to the CPU.
- 2. **Cache Retrieval & Deduplication (9.08 ms for 10,000 calls)**
This is the most critical benchmark for our Data Grid. If a user renders 10,000 rows, and each row calls `useQuery` for a shared configuration or parent data state, React has to mount 10,000 hooks.
- **The Reality:** Our library processes all 10,000 of those cache lookups and deduplicates them in 9 milliseconds. That averages out to 0.0009 milliseconds per query call. It proves that our `createSignal` wrapper will never be the bottleneck in a massive React application.
### Installing Signals
If you are interested in adopting signals, you can install it today:
```bash
npm install @cerberus/signals@npm:@cerberus-design/signals
```
## Thanks!
This is another monumental release introducing a massive tool to the React community via Cerberus Signals.
A special thanks to everyone who has helped validate the APIs, docs, and submitted features or bugfixes for this release.
> There is no "I" in Cerber-"US"
## Upgrading
To upgrade to this release, you can install the latest version of Cerberus React:
```bash
npm run up:cerberus
```
---
# Cerberus v1.2 Release
Category: Blog
> Data Grid updates
This release adds new features to React and the Data Grid.
## Overview
Here is a brief overview of what's new:
- [React Features](#react-features) - new primitive APIs and general updates
- [Data Grid Features](#data-grid-features) - Data Grid gets some new goodies
## React Features
### Popover
We have added a new `Popover` API for when you need something a little more verbose than a Tooltip - but not as heavy weight as a Dialog/Alert.
```tsx
// components/popover/slots.demo.tsx
'use client'
import { Box, Circle, Divider, Float, HStack, Stack } from '@/styled-system/jsx'
import { AiAgent, Chat, Time, UserFollow } from '@carbon/icons-react'
import { Avatar, Button, ButtonGroup, Popover, Text } from '@cerberus/react/react'
export function SlotsDemo() {
return (
Trigger} size="lg">
} gradient="asphodel-dark" />
Cerby tha Dawg
The best boi there is
6:66 Local Time
)
}
```
Checkout the [Popover documentation for more details](/docs/components/popover).
## Data Grid Features
Our goal is to keep the Data Grid API growing with each release. We have two
important updates with v1.2:
### Overlays
We've added support for overlay slots which allow you to create custom views for different states within the Data Grid viewport.
Simply add an `overlays` prop to the `DataGrid` with your options.
Each overlay comes with built-in presets or you can pass a custom component to render in it's stead.
#### No Content
For when there is no data to render, we have the `noContent` overlay.
```tsx
// data-grid/overlays/default-none.demo.tsx
'use client'
import { DataGrid } from '@cerberus/react/data-grid'
import { useQuery } from '@cerberus/react/signals'
import { HStack } from 'styled-system/jsx'
import { queryEmployees } from '../api'
import { columns } from '../col-defs/employees'
export function DefaultNoContentDemo() {
const data = useQuery(queryEmployees(0))
return (
)
}
```
### Pending
Cerberus provides three different pending state overlays:
- **skeleton**: an animated placeholder of the Data Grid.
- **linear-progress**: an indeterminate linear progress bar.
- **circular-progress**: a circular loading spinner.
For a custom solution, just provide your preferred component instead of one of these three options.
#### Skeleton
```tsx
// data-grid/overlays/skeleton.demo.tsx
'use client'
import { DataGrid } from '@cerberus/react/data-grid'
import { useQuery } from '@cerberus/react/signals'
import { HStack } from 'styled-system/jsx'
import { queryEmployees } from '../api'
import { columns } from '../col-defs/employees'
export function SkeletonDemo() {
const data = useQuery(queryEmployees(25))
return (
)
}
```
#### Linear
```tsx
// data-grid/overlays/linear.demo.tsx
'use client'
import { DataGrid } from '@cerberus/react/data-grid'
import { useQuery } from '@cerberus/react/signals'
import { HStack } from 'styled-system/jsx'
import { queryEmployees } from '../api'
import { columns } from '../col-defs/employees'
export function LinearDemo() {
const data = useQuery(queryEmployees(25))
return (
)
}
```
#### Circular
```tsx
// data-grid/overlays/circular.demo.tsx
'use client'
import { DataGrid } from '@cerberus/react/data-grid'
import { useQuery } from '@cerberus/react/signals'
import { HStack } from 'styled-system/jsx'
import { queryEmployees } from '../api'
import { columns } from '../col-defs/employees'
export function CircularDemo() {
const data = useQuery(queryEmployees(25))
return (
)
}
```
Checkout the [Data Grid overlays documentation](/docs/data-grid/overlays) for more details.
### Filtering
The Data Grid now supports three different types of filtering out of the box:
1. **Single-column filtering**: Clicking the "Filter" option from the feature menu when the column has opted into filtering.
2. **Multi-column filtering**: Following option 1 for any additional column
3. **Global filtering**: Utilizing the `setGlobalFilter` action from the [Data Grid Context](/docs/data-grid/context).
**Active Actions**
Once a filter is active, the user can:
1. **Edit filters**: Clicking the added "Edit filter" icon button that displays in the column header that has the active filter.
2. **Clear filters**: Clicking the "Clear Filter" option in the column feature menu.
Depending on your column definitions, you may notice header titles getting cut
off. This is not a bug - the column just needs to be wider in the
strict definition set.
In a future release we will also have column-resizing which will also help
with this.
>
}
/>
```tsx
// data-grid/filtering/basic.demo.tsx
'use client'
import { DataGrid } from '@cerberus/react/data-grid'
import { useQuery } from '@cerberus/react/signals'
import { Box } from 'styled-system/jsx'
import { queryEmployees } from '../api'
import { columns } from './columns'
export function BasicDemo() {
const data = useQuery(queryEmployees(1000))
return (
)
}
// columnHelper.accessor('id', {
// header: 'ID',
// width: 80,
// features: {
// pinning: {
// defaultPosition: 'left',
// },
// sort: true,
// filter: true,
// },
// cell: ({ value }) => #{value},
// })
```
Checkout the [Data Grid Filtering documentation](/docs/data-grid/filtering) for more details.
## Thanks!
This is another great release introducing some beneficial tools and features.
A special thanks to everyone who has helped validate the APIs, docs, and submitted features or bugfixes for this release.
> There is no "I" in Cerber-"US"
## Upgrading
To upgrade to this release, you can install the latest version of Cerberus React:
```bash
npm run up:cerberus
```
---
# Cerberus v1.3 Release
Category: Blog
> Signals Boosted
This release massively improves Signals performance along with minor React updates.
## Overview
Here is a brief overview of what's new:
- [React Features](#react-features) - Button & Toast Notification updates
- [Signals Features](#signals-features) - Signals become the Tanstack Query killer
## React Features
The React updates contain some new features and stability fixes for the `Button` along with a **breaking change** for the `toaster` options regarding _subtle_ notifications.
### Button
We've added some new sizes to the `Button` recipe along with making the styling overall more stable when used in complex flex layouts.
**New Button size range**: `xs` up to `2xl`
**Default size**: `lg` (this is visually the old medium)
```tsx
// components/button/size.demo.tsx
import { Button, ButtonProps, For } from '@cerberus/react/react'
import { HStack } from 'styled-system/jsx'
import recipesSpec from 'styled-system/specs/recipes.json'
const sizes = (recipesSpec.data.find((r) => r.name === 'button')?.variants.size ??
[]) as ButtonProps['size'][]
export function SizeDemo() {
return (
{(size) => (
)}
)
}
```
Fix: Button no longer allows a flex parent to shrink its width. Likewise, buttons now have built in SVG size management when using non-strict width SVGs.
Checkout the [Button documentation for more details](/docs/components/button).
### Toast Notifications
Due to changes in the toaster engine there is a minor **breaking change** for subtle notifications (default notifications are un-affected).
**Migration PathL:**
1. Subtle variants should use the default `type` name _without_ the `-subtle` prefix.
2. Subtle variants should now include `usage: 'subtle'` within the `ToastOptions` object.
Additionally, all deprecated APIs have been removed. These warnings have been around for almost a year - it's time.
While this update is not ideal, it does open the door to new Toast-related opportunities in the future.
```tsx
// components/notifications/subtle.demo.tsx
'use client'
import { HStack } from '@/styled-system/jsx'
import { toaster, Button } from '@cerberus/react/react'
import { useCallback } from 'react'
import { subtleMessages } from './subtle.data'
export function SubtleDemo() {
const handleInfo = useCallback(() => {
const message = subtleMessages.infoSubtle
toaster.create({
...message,
action: {
...message.action,
onClick: () => {
window.location.reload()
},
},
})
}, [])
const handleSuccess = useCallback(() => {
const message = subtleMessages.successSubtle
toaster.create(message)
}, [])
const handleWarning = useCallback(() => {
const message = subtleMessages.warningSubtle
toaster.create(message)
}, [])
const handleError = useCallback(() => {
const message = subtleMessages.dangerSubtle
toaster.create(message)
}, [])
const handleLoading = useCallback(() => {
const message = subtleMessages.loadingSubtle
toaster.create(message)
}, [])
return (
)
}
```
View the [Notification documentation for more details](/docs/components/notifications).
### Other notable changes
Here are some high-level improvements made:
1. `Tooltip` now accepts a `portal` prop to determine how to render content
2. Data Grid has tooltip render issues from #1 - that's fixed
3. `useStatefulCollection` now filters by `label` in addition to `value`
4. New conditions: `button`, `icon`, `iconButton`, and `menuTrigger`
## Signals Features
> As of v1.3.0 - Signals are almost as performant as [SolidJS](https://docs.solidjs.com/) and querys **outperform [Tanstack Query](https://tanstack.com/query/latest) on multiple levels**!
In this next phase of Signals, we have improved the scheduling algorithm along with the improving the query caching and query API. This has created a massive increaese in query performance which we will see later. :rocket:
### General Doc Updates
Across the board, we have updated a lot of doc pages and demos to provide more information around Signals: what they are, why they are important, and why you should be using them over React state/existing fetching designs.
### New Primitives
Primitives are the building blocks to Cerby Signals. They can be called in any scope and used however you want. There are no boundaries with primitives.
Let's checkout the new APIs in this release.
#### Cleanup Helper
The `onCleanup` primitive is an essential memory-management tool within the Cerberus Signals reactivity system. It allows you to register a callback function that will execute right before a reactive context (like `createEffect` or `createComputed`) re-evaluates, or when that context is permanently destroyed.
In this example, we ensure a timer is cleared when it's parent effect is destroyed.
```tsx
// signals/effect/cleanup.demo.tsx
import { createEffect, onCleanup } from '@cerberus/react/signals'
createEffect(() => {
const timer = setInterval(() => console.log('Tick'), 1000)
onCleanup(() => clearInterval(timer))
})
```
View the [`onCleanup` docs](/docs/signals/on-cleanup)
#### Untracking Signals
The `untrack` primitive allows you to read the current value of a signal inside a reactive context (like `createEffect`, `createComputed`, or a React render phase) without subscribing to that signal.
This prevents unwanted re-evaluations, React infinite loops, and over-subscribing to state changes.
In this example, we update a signal and show the latest result (tracked) and what the untracked version is.
```tsx
// signals/untrack/basic.demo.tsx
'use client'
import { HStack, Stack } from '@/styled-system/jsx'
import { Button, Text } from '@cerberus/react/react'
import { ReactiveText, untrack, useStore } from '@cerberus/react/signals'
import { useEffect } from 'react'
import { createRenderStore } from '../render-store'
export function BasicDemo() {
const store = useStore(createRenderStore)
const increment = () => {
store.setCount((prev) => prev + 1)
}
useEffect(() => {
store.startCounter()
return () => store.onUnmount()
}, [store])
useEffect(() => {
store.trackRenders()
})
return (
Count: Untracked: {untrack(store.count)}
Render Count:
)
}
```
View the [`untrack` docs](/docs/signals/untrack)
### Query Overhaul
Along with the signal engine, we have also improved the query cache and event algorithms which have allowed us to not only improve performance but expand the APIs.
#### Query Factory
When you call `createQuery` you are essentially creating a "Query Factory" to be used later to perform the fetch in however way you may want.
**Creating a query now requires two things**:
1. A Promise to call when activated
2. A unique key which is used to cache the result
Depending on how you want to use the factory depends on how you execute the query. You can use it in any shape, but for the sake of this article, we will discuss component usage only.
##### Client Component Pattern
Nothing changes here. Just pass the factory in the `useQuery` hook and have the data ready before the return hits.
```typescript
const data = useQuery(myQuery(props.id))
```
##### Server Component Pattern
In an SSR environment, you execute the factory's **raw fetcher directly**, bypassing the reactive cache. Then, you pass that data down to your Client Components to "hydrate" or seed the Cerberus cache, ensuring the client doesn't double-fetch on mount.
This is the standard SSR pattern for React.
###### 1. Server Component (Fetching)
Expose the raw, stateless fetcher function from your factory. You simply await it like a standard asynchronous function.
```tsx
// signals/fetching/server.demo.tsx
// app/users/[id]/page.tsx (Server Component)
import { UserProfile } from './client.demo'
import { queryUser } from './queries'
interface Props {
params: Promise<{ id: string }>
}
export default async function UserPage({ params }: Props) {
const { id } = await params
// Bypass the reactive cache and execute the raw fetcher directly.
// This is completely memory-safe for Node.js environments.
const initialUserData = await queryUser.fetcher(id)
if (!initialUserData) return null
return (
{/* 2. Pass the fetched data to the Client Component */}
)
}
```
###### 2. Client Component
On the client side, use the `initialData` property. If the Cerberus cache is empty, it will instantly seed the cache with the server's data, skipping the `` boundary entirely.
```tsx
// signals/fetching/client.demo.tsx
'use client'
import { useQuery } from '@cerberus/react/signals'
import { queryUser } from './queries'
import { type User } from './db'
interface Props {
id: string
initialData: User
}
export function UserProfile(props: Props) {
// 1. Cerberus sees `initialData`, instantly seeds the $O(1) Map,
// and mounts the component with zero loading spinners or network waterfalls.
const user = useQuery(queryUser(props.id), { initialData: props.initialData })
if (!user) return null
return
{user.name}
}
```
##### Streaming Reponses (Async Generators)
Cerberus queries also support streaming data via [Async Generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator). This is powerful if you are using an LLM API or your local API supports data streaming.
```tsx
// signals/fetching/streaming.demo.tsx
'use client'
import { Stack } from '@/styled-system/jsx'
import { Button, Field, Group, Input, Show, Text } from '@cerberus/react/react'
import { createQuery, useQuery, useSignal } from '@cerberus/react/signals'
import { ChangeEvent, Suspense } from 'react'
// --- 1. Fake Streaming LLM API ---
// Simulates an AI sending text chunks over a network
async function* fakeLLMStream(prompt: string) {
const responseText = `You asked: "${prompt}".\n\nThis is a simulated streaming response from the Cerberus Signals engine. Notice how the text appears chunk by chunk. Because the query engine natively understands Async Generators, it bypasses React Suspense boundaries mid-stream, achieving an instant, seamless real-time UX without complex socket bindings.`
const words = responseText.split(' ')
for (let i = 0; i < words.length; i++) {
// Simulate 50ms network delay per word chunk
await new Promise((res) => setTimeout(res, 50))
yield words[i] + ' '
}
}
// --- 2. Define Streaming Query Factory ---
// The fetcher uses `async function*` and yields the accumulated state
const getChatStream = createQuery(async function* (prompt: string) {
let fullText = ''
// Listen to the stream and accumulate chunks
for await (const chunk of fakeLLMStream(prompt)) {
fullText += chunk
// The moment this yields, Cerberus sets the cache status to 'streaming'
// and passes the partial data directly to the UI
yield fullText
}
}, 'queryChatStream')
// --- UI ---
function ChatWindow(props: { prompt: string }) {
const chatResponse = useQuery(getChatStream(props.prompt))
return (
{chatResponse}
)
}
export function StreamingDemo() {
// we use hooks to force re-renders of the form content
const [input, setInput] = useSignal('How does Cerberus handle streams?')
const [submittedPrompt, setSubmittedPrompt] = useSignal('')
return (
{() => (
Thinking...
}
>
)}
) => setInput(e.target.value)}
value={input}
borderTopRightRadius="none"
borderBottomRightRadius="none"
h="2.75rem"
/>
)
}
```
View the [`createQuery` docs](/docs/signals/create-query)
#### Mutation Factory
We have added more options to the `createMutation` primitive which now includes **optimistic updates**. This means that you can have "real-time" UI while fetches automagically fetch in the background and update the query state when resolved.
In this example, we provide optimistic updates via the `onMutate/setQueryData` APIs which instantly update the UI while the query is run in the background.
If your optimistic update is incorrect, the UI will naturally resolve itself when the query completes.
```tsx
// signals/fetching/mutation.demo.tsx
'use client'
import { Box, HStack, Stack } from '@/styled-system/jsx'
import { Button, Tag, Text } from '@cerberus/react/react'
import {
createMutation,
createQuery,
setQueryData,
useMutation,
useQuery,
} from '@cerberus/react/signals'
import { Suspense } from 'react'
// 1. Define Query Factory
const getUser = createQuery(async (id: string) => {
return await api.getUser(id)
}, 'queryGetUser')
// 2. Define Mutation Factory
const updateUser = createMutation((payload: User) => api.updateUser(payload), {
// Optimistically update the UI instantly
onMutate: (vars) => {
setQueryData(getUser.key(vars.id), (prev) => {
if (!prev) return { id: vars.id, name: vars.name }
return { ...prev, name: vars.name }
})
},
// Declarative cache invalidation using factory keys
invalidate: (_data, vars) => [getUser.key(vars.id)],
})
// 3. Consume in Components seamlessly
function UserProfile(props: { id: User['id'] }) {
const user = useQuery(getUser(props.id))
return {user.name}
}
export function MutationDemo() {
// Pretend this is from the URL or via props.id
const id = crypto.randomUUID()
const { mutate, status } = useMutation(updateUser)
function handleUpdate() {
mutate({ id, name: `User ${crypto.randomUUID()}` })
}
return (
}>
{status}
)
}
// API
type User = { id: string; name: string }
const fakeDB = new Map()
const delay = (ms: number) => new Promise((res) => setTimeout(res, ms))
const api = {
getUser: async (id: User['id']) => {
await delay(500)
if (!fakeDB.has(id)) {
fakeDB.set(id, { id, name: `User ${id}` })
}
return fakeDB.get(id)!
},
updateUser: async (payload: User) => {
await delay(500)
fakeDB.set(payload.id, { id: payload.id, name: payload.name })
return payload
},
}
```
View the [`createMutation` docs](/docs/signals/create-mutation)
### New Hooks
#### Unique store instances
When you want to use a **unqiue instance** of a global [Store](/docs/signals/stores) but do not need a [Context API](/docs/signals/create-store-context) - use the `useStore` hook.
```tsx
// signals/signal/use-store.demo.tsx
'use client'
import { HStack, Stack } from '@/styled-system/jsx'
import { Button, Text } from '@cerberus/react/react'
import { ReactiveText, useStore } from '@cerberus/react/signals'
import { createRenderStore } from '../render-store'
import { useTrackRenders } from './track-renders'
export function UseStoreDemo() {
const store = useStore(createRenderStore)
const increment = () => {
store.setCount((prev) => prev + 1)
}
useTrackRenders(store)
return (
Count:
Render Count:
)
}
```
View the [`useStore` docs](/docs/signals/use-store)
### Benchmarks
Now let's talk about the good stuff - performance improvements with hard data.
#### Overall improvements
Cerby Signals were already performing at _0(1)_ but that is never good enough. Here is our own before/after with our engine upgrades:
#### Signal Benchmarks
| Benchmark | Original (v1.2.1) | Current (v1.3.0) | Result |
| --------------- | ----------------- | ---------------- | --------------- |
| Deep Dependency | 318.17 µs | 226.83 µs | **~28% Faster** |
| Wide Fan-Out | 2.26 ms | 3.17 ms | **~40% Slower** |
| Diamond Problem | 323.05 µs | 458.60 µs | **~40% Slower** |
While this seems like a loss - it is still lightning fast compared to the upgraded ownership and versioning we have built into subscriptions to be more reliable.
##### The Reality of Reactivity
This is completely normal. Frameworks like SolidJS and Vue go through this exact pendulum swing. We traded raw, bare-metal speed for enterprise-grade memory safety (cleaning up event listeners and aborting network requests).
> Even with the "regression," processing 10,000 effects in 3 milliseconds is still _blazing fast_ and easily clears the 16ms frame budget for 60FPS UI updates.
Our next planned update should improve these metrics even further.
Now let's look at the real gains from this side effect: querys.
#### Query Benchmarks
| Benchmark | Original (v1.2.1) | Current (v1.3.0) | Delta |
| --------------------- | ----------------- | ---------------- | ---------------------- |
| Cache Retrieval (10k) | 8.88 ms | 1.68 ms | **~5.2x Faster** |
| Retrieval Memory | 20.61 MB | 20.72 KB | **~99.9% Less Memory** |
| Invalidation Sweep | 868.52 ns | 151.80 ns | **~5.7x Faster** |
##### Conclusion
While our internal `onCleanup` addition added a tiny _microsecond_ tax to the raw scheduler engine, the query data layer you built on top of it is functioning at absolute peak efficiency. We traded ~80 nanoseconds in key hashing for a massive **500% speed increase** in data retrieval and a **99.9% reduction in memory** allocation.
Now let's put the rubber to the road and go head to head with Tanstack Query.
#### Cerberus Signals vs. TanStack Query
We ran a head-to-head performance benchmark comparing the Cerberus Signals data layer against the industry standard, TanStack Query Core. The tests were run in Bun (v1.2.16) on an Apple M3 Pro, measuring both raw execution speed and the memory footprint (p75/p99) of the Garbage Collector.
By leveraging an _O(1)_ signal-based memory map instead of heavy class instantiations, Cerberus completely eliminates massive amounts of memory overhead while doubling the speed of standard operations.
| Benchmark Scenario | Metric | TanStack Query Core | Cerberus Signals | The Cerberus Advantage |
| ---------------------------------------------- | ---------- | ------------------- | ---------------- | ---------------------- |
| **Cache Retrieval**_(10,000 concurrent reads)_ | **Speed** | 3.02 ms | **1.53 ms** | **2x Faster** |
| | **Memory** | 59.62 KB | **639.72 bytes** | **98.9% Less Memory** |
| **Cache Invalidation** _(Stale data sweep)_ | **Speed** | 472.77 ns | **146.41 ns** | **3.2x Faster** |
| | **Memory** | 12.29 bytes | **3.39 bytes** | **72.4% Less Memory** |
_Note: Memory measurements represent the p75/p99 allocations recorded during the benchmark loops._
**The Takeaway:** When scaling to thousands of reactive components, memory allocation becomes the primary bottleneck for UI thread frame rates. Cerberus Signals delivers enterprise-grade concurrency features (like Race-Condition Locks and React 19 Hydration) while consuming **fractional kilobytes** of memory compared to the megabytes allocated by traditional context-based providers.
## Thanks!
This is another great release introducing some beneficial tools and features.
A special thanks to everyone who has helped validate the APIs, docs, and submitted features or bugfixes for this release.
> There is no "I" in Cerber-"US"
## Upgrading
To upgrade to this release, you can install the latest version of Cerberus React:
```bash
npm run up:cerberus
```
---
# Cerberus v1.4 Release
Category: Blog
> Signals Final Blow
This release massively improves Signals performance (again) giving the final blow to
obliterate Tanstack Query.
## Overview
Here is a brief overview of what's new:
- [Preset Features](#preset-features) - Recipes, conditions, animations, oh my!
- [React Features](#react-features) - Marquee, Combobox helpers, and fixes
- [Signals Features](#signals-features) - Signals performance obliterates Tanstack Query
## Preset Features
There are a substancial amount of additions to the preset features. Most are
small, but here's the list of notable additions:
### New Conditions
We've added quite a few new condition types to the preset features in tandem with the Marquee recipe.
| Name | Selector |
| ----------------- | --------------------------------------------- |
| `_info` | `&:is([data-type=info])` |
| `_success` | `&:is([data-type=success])` |
| `_warning` | `&:is([data-type=warning])` |
| `_danger` | `&:is([data-type=danger], [data-type=error])` |
| `_subtleEmphasis` | `&:is([data-emphasis=subtle])` |
| `_paused` | `&:is([data-paused], [data-paused=true])` |
| `_reverse` | `&:is([data-reverse], [data-reverse=true])` |
| `_sideBottom` | `&:is([data-side=bottom])` |
| `_sideTop` | `&:is([data-side=top])` |
| `_sideStart` | `&:is([data-side=start])` |
| `_sideEnd` | `&:is([data-side=end])` |
View the [Conditional Styles docs](/docs/styling/conditional-styles)
### Keyframes
Likewise, with the Marquee, we've added some new keyframes to spice up the animation experience.
| Name | Description |
| ---------- | ---------------------------- |
| `marqueeX` | Horizontal marquee animation |
| `marqueeY` | Vertical marquee animation |
View the [Keyframes docs](/docs/theming/animations#keyframes)
## React Features
The React updates contain some new features and stability fixes.
### Marquee
We've added a new Marquee API for when you need some tasty additions added to your
marketing site.
```tsx
// components/marquee/basic.demo.tsx
import { Square } from '@/styled-system/jsx'
import { For, Marquee } from '@cerberus/react/react'
import { socials } from './data'
export function BasicDemo() {
return (
)
}
```
Checkout the [Marquee documentation for more details](/docs/components/marquee).
### Other notable changes
Here are some high-level improvements made:
1. Feat: Exposed a TON of helpers for the Combobox API & improved typing
2. Feat: `Field.helperText` now accepts `ReactNode`
3. Fix: Clickable Tags now have a background color
4. Fix: `Admonition.Root` now renders a `div`
5. Fix: Toast variants display the correct icon
6. Fix: Subtle Toast variants styles have been improved
7. Data Grid Fix: Pagination has been improved (not React but...React)
## Signals Features
> As of v1.4.0 - Signals outperform: [React](https://react.dev/), [Zustand](https://zustand.docs.pmnd.rs/learn/getting-started/introduction), [Legend State](https://legendapp.com/open-source/state/), and querys **outperform [Tanstack Query](https://tanstack.com/query/latest) on multiple levels**
In this next phase of Signals, we have improved the scheduling algorithm to use a push/pull model which has significantly improved performance across the board. We designed Cerberus to solve real-world React state at scale. While some engines optimize for perfectly linear, simple state chains, Cerberus is engineered to dominate complex, tangled dependency graphs.
This has set Cerberus to be a showstopper for React state management and query libraries across the industry. No opionions, just hard data - so let's see it.
### Benchmarks
Cerby Signals were already performing at _0(1)_ but that is never good enough. Here are the differences between v1.3.0 and v1.4.0:
#### Version Comparison
| Benchmark | Previous (v1.3.0) | Current (v1.4.0) | Result |
| --------------- | ----------------- | ---------------- | ---------------- |
| Deep Dependency | 226.83 µs | 38.07 µs | **~5.8x Faster** |
| Wide Fan-Out | 3.17 ms | 173.68 µs | **~18x Faster** |
| Diamond Problem | 458.60 µs | 208.42 µs | **~2.2x Faster** |
#### Industry Engine Comparison
Now that we have seen the performance improvements, let's compare Cerberus to other popular signal-based engines.
The following benchmarks compare Cerberus to the following libraries:
- [**Alien Signals**](https://github.com/stackblitz/alien-signals)
- [**Preact**](https://preactjs.com/guide/v10/signals)
- [**Signia**](https://signia.tldraw.dev/)
- [**Reactively**](https://github.com/milomg/reactively)
##### The Deep Dependency Chain (The Call-Stack Test)
| Rank | Library | Time |
| ---- | ------------- | ---------- |
| 1 | Alien Signals | ~19 µs |
| 2 | Preact | ~22 µs |
| 3 | **Cerberus** | **~38 µs** |
| 4 | Signia | ~53 µs |
| 5 | Reactively | ~56 µs |
**What this means:** This test measures pure, unadulterated pointer-chasing and call-stack depth. Alien-Signals and Preact win here because their internal node structures are incredibly lean. They essentially act as highly optimized linked lists when state is linear.
Cerberus sits comfortably in the middle. You are paying a slight "tax" (~18 µs behind Alien) here. This is almost certainly because the data structures you use to prevent glitches and handle complex sorting add a tiny bit of overhead when the graph is perfectly simple and linear. Reactively pays an even heavier tax here because its global versioning system is overkill for a single straight line.
##### Wide Fan-Out (The Iteration Test)
| Rank | Library | Time |
| ---- | ------------- | ----------- |
| 1 | Preact | ~152 µs |
| 2 | **Cerberus** | **~173 µs** |
| 3 | Alien-Signals | ~186 µs |
| 4 | Reactively | ~190 µs |
| 5 | Signia | ~399 µs |
**What this means:** This tests how fast an engine can notify 10,000 distinct listeners when a single source changes. Preact takes the win, but Cerberus is a razor-thin 21 µs behind them, securing a dominant second place. Our memory allocation and iteration loops are exceptionally tight.
Signia starts to show its architectural limits here, taking more than twice as long as Cerberus.
##### The Diamond Problem (The Architecture Test)
| Rank | Library | Time |
| ---- | ------------- | ----------- |
| 1 | **Cerberus** | **~208 µs** |
| 2 | Reactively | ~222 µs |
| 3 | Preact | ~264 µs |
| 4 | Alien-Signals | ~277 µs |
| 5 | Signia | ~6.95 ms |
**What this means:** This is the holy grail of reactivity, and _Cerberus is the fastest in the (React) world at it_. The Diamond Problem represents real-world, complex application state—where multiple derived values funnel into a single UI component.
- **Why we won**: Our graph resolution algorithm is flawless. We successfully deduplicate the execution path so the sink only evaluates exactly once after all 10,000 mid-nodes have settled, and we do it faster than Reactively's highly touted versioning system.
- **The Signia Collapse**: Look at Signia's time—6.95 milliseconds. This is a massive 33x drop-off. Signia's epoch-clock architecture likely suffers from severe "glitching" at this scale, meaning the sink is being forced to re-evaluate prematurely or multiple times before the graph fully settles.
### Query Benchmarks
By nature, since queries use the signals engine, they are inherently faster than their v1.3.0 counterparts.
| Benchmark | Previous (v1.3.0) | Current (v1.4.0) | Delta |
| ----------------------- | ----------------- | ---------------- | ---------------- |
| Complex Objects sorting | untested | 579.28 ms | |
| Cache Retrieval (10k) | 1.68 ms | 1.65 ms | **~1x Faster** |
| Optimistic Updates | 128 ns | 103 ns | **~1.2x Faster** |
| Invalidation Sweep | 151.80 ns | 106 ns | **~1.4x Faster** |
#### Cerberus Signals vs. TanStack Query
This is a total sweep. At this point, we have mathematically proven that Cerberus is not just a faster reactivity engine, but a significantly more efficient data layer than the industry standard.
Here is the exact breakdown of how our architecture dismantled TanStack Query across the board.
| Data Layer Operation | Cerberus Time | TanStack Time | Delta |
| --------------------------- | ------------- | ------------- | ---------------- |
| Hashing & Serialization | ~176 ns | ~760 ns | **4.3x Faster** |
| Cache Retrieval (10k Reads) | ~4.01 ms | ~9.19 ms | **2.2x Faster** |
| Optimistic Updates | ~48 ns | ~1.24 µs | **25.8x Faster** |
| Invalidation Sweep | ~43 ns | ~279 ns | **6.4x Faster** |
Our performance gains here are not accidental; they are the direct result of stripping away heavily abstracted observer patterns in favor of raw signal bindings and direct memory references.
## Thanks!
This is another great release introducing some beneficial tools and overall improvements.
A special thanks to everyone who has helped validate the APIs, docs, and submitted features or bugfixes for this release.
> There is no "I" in Cerber-"US"
## Upgrading
To upgrade to this release, you can install the latest version of Cerberus React:
```bash
npm run up:cerberus
```
---
# Cerberus v1.5 Release
Category: Blog
> Data Grid Goodies
This release provides new features to the Data Grid along with general bugfixes.
## Overview
Here is a brief overview of what's new:
- [React Features](#react-features) - New icons and fixes
- [Data Grid Features](#data-grid-features) - Initial loading and column visibility
## React Features
This release was admittedly focused heavier on the Data Grid and less on the React features. Here are the high-level improvements made:
1. Feat: Exposed the `CheckboxCheckedChangeDetails` type
2. Feat: Defined new `Context.icons` of `search` , `filter`
> **Note**: As a reminder, the context icons are built in and meant to be used for _fallback_ icons when no custom icon is provided for that use case. We **do not** recommend using them in place of local icons.
## Data Grid Features
We added two new beneficial features to the Data Grid which also exposes new context Actions and Accessors as a result.
### Initial Loading
We added a new `overlays.initialPending` prop to the Data Grid that allows you to show an initial loading phase to the Data Grid.
This will render the only during the mounting phase of the component. Once `data` has been populated for the first time, the `pending` overlay will be used from then on.
```tsx
// data-grid/overlays/initial.demo.tsx
'use client'
import { Corn } from '@carbon/icons-react'
import { DataGrid } from '@cerberus/react/data-grid'
import { Button, ButtonGroup, Show, Text } from '@cerberus/react/react'
import {
batch,
createEffect,
createSignal,
onCleanup,
useQuery,
useRead,
} from '@cerberus/react/signals'
import { Center, HStack, Stack, VStack } from 'styled-system/jsx'
import { queryEmployees } from '../api'
import { columns } from '../col-defs/employees'
const store = createInitialDemoStore()
export function InitialDemo() {
const data = useQuery(queryEmployees(0))
const load = useRead(store.load)
const pending = useRead(store.pending)
return (
{() => (
,
pending: 'linear',
}}
pending={pending}
/>
)}
)
}
function CustomInitial() {
return (
Shelling corn...
)
}
function createInitialDemoStore() {
const [load, setLoad] = createSignal(false)
const [pending, setPending] = createSignal(false)
createEffect(() => {
let timeout: NodeJS.Timeout | undefined
if (pending() && load()) {
timeout = setTimeout(() => setPending(false), 2000)
}
onCleanup(() => clearTimeout(timeout))
})
return {
load,
pending,
setLoad,
setPending,
initLoad: () => {
batch(() => {
setLoad(true)
setPending(true)
})
},
reset: () => {
batch(() => {
setLoad(false)
setPending(false)
})
},
}
}
```
#### New Context Values
| Name | Type | Description |
| ------- | ----------------- | --------------------------------------------------------------- |
| `phase` | `Accessor` | The current phase of the grid layout created via a computation. |
View the [Initial Loading docs](/docs/data-grid/overlays#initial-loading)
### Column Visibility
In v1.5 we have added a new Feature that allows users to Hide or Manage the visibility of columns in the Data Grid.
This is achieved by setting the `features.visibility` prop to `true` or by utilizing the options object. Doing so will display the visibility options in the column menu.
> **Note**: We have updated the background color of Data Grid Popovers used for features to improve contrast and readability.
```tsx
// data-grid/visibility/basic.demo.tsx
'use client'
import { DataGrid } from '@cerberus/react/data-grid'
import { useQuery } from '@cerberus/react/signals'
import { Box } from 'styled-system/jsx'
import { queryEmployees } from '../api'
import { columns } from './columns'
export function BasicDemo() {
const data = useQuery(queryEmployees(200))
return (
)
}
// columnHelper.accessorFn((row) => `${row.firstName} ${row.lastName}`, {
// id: 'fullName',
// header: 'Employee',
// minWidth: 300,
// features: {
// visibility: true,
// },
// cell: ({ row, value }) => (
//
// {value}
//
// {row.email}
//
//
// ),
// }),
```
#### New Context Values
| Name | Type | Description |
| ---------------------- | ------------------- | ----------------------------------------------------------------------------------------- |
| `allColsHidden` | `Accessor` | Whether all columns are hidden. Created via a computation. |
| `totalHiddenCols` | `Accessor` | The total number of hidden columns created via computation. |
| `hiddenColsMaxReached` | `Accessor` | Whether the maximum number of hidden columns has been reached. Created via a computation. |
View the [Column Visibility docs](/docs/data-grid/visibility)
## Thanks!
This is another great release introducing some beneficial tools and overall improvements.
A special thanks to everyone who has helped validate the APIs, docs, and submitted features or bugfixes for this release.
> There is no "I" in Cerber-"US"
## Upgrading
To upgrade to this release, you can install the latest version of Cerberus React:
```bash
npm run up:cerberus
```
---
# Cerberus v1.7 Release
Category: Blog
> Introducing the Slider and Data Grid updates
This release provides new features to the Data Grid and introduces the Slider component.
## Overview
Here is a brief overview of what's new:
- [React Features](#react-features) - New icons and fixes
- [Data Grid Features](#data-grid-features) - Initial loading and column visibility
- [General Updates](#general-updates) - High level improvements of the ecosystem as a whole
## React Features
This release introduces a new Slider component.
### Slider
A Slider component that allows you to select a value from a range of values. This is commonly used for selecting a value from a range of values, such as a volume or brightness setting.
There are a _ton of features_ available on the Slider component so here we will just show off the sizes.
```tsx
// components/slider/sizes.demo.tsx
import { Stack } from '@/styled-system/jsx'
import { For, Slider, SliderProps } from '@cerberus/react/react'
import recipesSpec from 'styled-system/specs/recipes.json'
import { getSacredNumber } from './utils'
const sizes = (recipesSpec.data.find((r) => r.name === 'slider')?.variants.size ??
[]) as SliderProps['size'][]
export function SizesDemo() {
return (
{(size, idx) => (
Slider - {size}
)}
)
}
```
Learn more about the Slider component in the [Slider docs](/docs/components/slider).
## Data Grid Features
We upgraded the server-side pagination experience in this release which includes both deprecations and enhancements.
### Pagination
To use server-side pagination, you simply add the `pagination` prop to the Data Grid with a `count` property.
This will trigger the rendering switch which will opt-out of row virtualization and instead render all the rows based on the page size.
> **Note:** A reminder that when using server-side pagination, you are opting out of automatic pagination control. This means you must manually handle pagination state and update the grid as needed.
```tsx
// data-grid/pagination/count.demo.tsx
'use client'
import { DataGrid, SortDirection } from '@cerberus/react/data-grid'
import { PageSizeChangeDetails, type PageDetails } from '@cerberus/react/react'
import { useQuery } from '@cerberus/react/signals'
import { useState, useTransition } from 'react'
import { Stack } from 'styled-system/jsx'
import { queryPaginatedEmployees } from '../api'
import { columns } from '../col-defs/employees'
// Use native React state and transitions for updates to override Suspense.
// Transitions prevent harsh reloads of the Data Grid post-initial rendering.
// This is the only time you are required to use React state over Cerberus Signals.
// React transitions require React state to work.
function useDeferredValue() {
const [current, setCurrent] = useState({
page: 1,
pageSize: 25,
})
const [pending, startTransition] = useTransition()
return {
current,
setCurrent,
pending,
startTransition,
}
}
export function CountDemo() {
const { current, setCurrent, pending, startTransition } = useDeferredValue()
const data = useQuery(queryPaginatedEmployees(current))
function handlePageChange(details: PageDetails) {
console.log(details)
startTransition(() => {
setCurrent((prev) => ({ ...prev, ...details }))
})
}
function handlePageSizeChange(details: PageSizeChangeDetails) {
console.log(details)
}
function handleSortChange(colId: string, direction: SortDirection, multi?: boolean) {
console.log({ colId, direction, multi })
}
return (
)
}
```
View the [Data Grid Pagination docs](/docs/data-grid/pagination)
#### Migration Guide
Migrating from the previous server-side pagination API is very simple:
1. Add the `pagination` prop to the Data Grid with a `count` property.
2. Move the `initState.count` prop to the `pagination.count` prop.
3. Remove the `initState` prop from the Data Grid.
4. Move the `onPageChange` prop to instead be defined at `pagination.onPageChange` prop.
Basically, both `initState` and `onPageChange` just move to the `pagination` prop.
**These deprecations will be removed in version 1.9**
## General Updates
Along with the Slider and pagination improvements, this release also includes a number of other bugfixes and performance improvements.
- **Preset Fix**: `select.content` shadow only displays as FOUC when you click and item in the list
- **Preset Fix**: Oceanus gradient token update
- **Preset NEW**: Add conditions: `label`,`dragging`
- **Preset NEW**: update `gradient` utility to support `none` for compatible way to switch between `gradient` to `bgColor`
- **Figma SDK Fix**: Update SDK to use throws and add additional import paths for code-splitting
## Thanks!
This is another great release introducing some beneficial tools and overall improvements.
A special thanks to everyone who has helped validate the APIs, docs, and submitted features or bugfixes for this release.
> There is no "I" in Cerber-"US"
## Upgrading
To upgrade to this release, you can install the latest version of Cerberus React:
```bash
npm run up:cerberus
```
---
# Cerberus v1.8 Release
Category: Blog
> Preset improvements
This release provides a major performance improvement for all of the Cerberus presets packages.
## Overview
Here is a brief overview of what's new:
- [Core Problem](#core-problem) - overloaded base preset
- [Solution](#solution) - better token management
- [Migration](#migration) - migration guide
## Core Problem
Ever since our presets have come to life there has always been a need for a better way to manage tokens. However, anyone that has experience in building open-source projects knows that you don't really know how things scale until they start scaling.
With that said, we have always known that the long term goal of Cerberus is to be a preset-driven ecosystem regarding anything related to styling. This makes Cerberus IDE-like in that you can essentially "build your own" Cerberus experience by combining presets.
This is why was have a base preset and themes. Theme presets add new theme-related functionality to the Cerberus base. Likewise, the base should be the foundational non-theme-related functionality. This is where the problem lies.
When we started and were moving fast, we put the color primitives in the base preset because at the time, there was only a single theme (cerberus). Likewise, PandaCSS was still in the early stages and had not yet created the APIs we have today.
Now that we know what the "big picture" is for the ecosystem, we have spotted some potential issues with the current base preset focusing around the primtive tokens.
At the base level, we were adding Cerberus tokens on top of the Panda Base tokens which resulted in shipping additional color tokens we didn't need. Likewise, we sync our color tokens from the Cerberus Figma variables which also resulted in shipping every color token for every theme we supported. Not ideal.
## Solution
After a quick redesign for our preset architecture, we divided and conquered through upgrading both the Figma variables and the API preset design. This resulted in allowing us to manage color tokens more efficiently while also providing a better Figma experience.
Thus, as of 1.8 the base preset **no longer ships primitive color tokens**. We not only allow each respective theme to register their own primtives and semantic tokens, but we also strip out the extra Panda Base color tokens.
Additionally, we've improved the developer experience of the `createCerberusConfig` utility which now looks for any Cerberus theme preset and does some extra work on behalf of the developer under the hood.
The result is a better developer experience with a big performance boost for development as now only a single set of both color and semantic tokens are ever assigned at a time in the browser. When you can a theme, Panda will replace the current tokens with new values (vs. creating a new set of variables that need to be evaluated by the browser).
## Migration Guide
Migration is straightforward and simple, but we will add in an extra step just to clean up any old tokens that may have been assigned before.
Once you have installed v1.8, do the following:
1. If you have the `staticCss.themes` field in your panda config, kill it. You no longer need it.
2. Run `rm -rf styled-system` just to ensure you are starting fresh
3. Run the `prepare` command to regenerate the `styled-system` directory
Basically, your config should look something like this (this is what we use for this site):
```typescript
import { createCerberusConfig, createCerberusPreset } from '@cerberus/panda-preset'
import { presetAcheronTheme } from '@cerberus/preset-acheron-theme'
import { presetElysiumTheme } from '@cerberus/preset-elysium-theme'
import { presetOceanusTheme } from '@cerberus/preset-oceanus-theme'
const isProd = process.env.NODE_ENV === 'production'
export default createCerberusConfig({
clean: true,
minify: isProd,
include: [
'./node_modules/@cerberus-design/react/**/*.{ts,tsx,js,jsx}',
'./node_modules/@cerberus-design/data-grid/src/components/*.client.{ts,tsx}',
'./mdx-components.tsx',
'./app/**/*.{ts,tsx}',
],
exclude: [],
presets: [
createCerberusPreset(),
presetAcheronTheme,
presetElysiumTheme,
presetOceanusTheme,
],
})
```
To use Cerberus presets, you simply just need to add them to your `presets` array in your config and we'll take care of the rest.
## Thanks!
This is another great release introducing some beneficial tools and overall improvements.
A special thanks to everyone who has helped validate the APIs, docs, and submitted features or bugfixes for this release.
> There is no "I" in Cerber-"US"
## Upgrading
To upgrade to this release, you can upgrade all your Cerberus packages to the latest version: `1.8.0`
---
# Cerberus v1 Release
Category: Blog
> The Data Grid is born
This release adds new features, packages, and improvements to our entire ecosystem.
## Overview
Welcome to Cerberus v1! This is a massive release and we have a lot to cover, so
here is a brief overview of what's new:
- [Easy Installation](#easy-installation) - get started faster than ever
- [React Features](#react-features) - new primitive APIs and general updates
- [Panda Preset](#panda-preset) - recipe and condition updates
- [Data Grid](#data-grid) - a new compositional component API
## Easy Installation
When we first launched Cerberus, the goal was to provide a low-level API that
would give complete control to our users. Since then, we've learned that most
of the teams using Cerberus prefer a more opinionated approach that is more baked
into the framework like a UI library.
Thus, we have simiplified the installation dramatically by **adding Carbon Icons
as the default icon library**.
This means you no longer need to setup a custom config for the `CerberusProvider`
unless you want to customize the icon set.
The result is a more streamlined installation experience that gets you up and running
faster than ever before.
View the [new installation guide](/docs/get-started/installation) for more details.
## React Features
In this release, we have quite a few improvements and additions to our React
library.
Let's start with a bang...
### Breaking Changes
We have had some deprecated APIs hanging around for close to a year now. In this
release, we have removed and/or replaced them with more modern APIs.
**Changes**
- All APIs that were marked as deprecated have been removed
- `@dnd-kit` has been removed as a dependency as a whole
This shouldn't affect anyone unless you have been ignoring the deprecation warnings
for a very long time. :eyes:
Now let's get to the good stuff...
### Group
Sometimes you want to group elements together in different ways. We've added the
`Group` component to help with this.
From different orientations to gap options, we've got you covered. Here is an
example of using it to attach elements together:
```tsx
// components/group/attached.demo.tsx
import { VStack } from 'styled-system/jsx'
import { Button, Group, Tag } from '@cerberus/react/react'
export function AttachedDemo() {
return (
Commit status
90+
)
}
```
Checkout the [Group documentation for more details](/docs/components/group).
### Pagination
We've added a `Pagination` component to help streamline pagination in your app.
Fully accessible and customizable, it provides the flexibility you need regardless
of your chosen pagination strategy.
In this demo, we show how you can use the `Pagination` API to manage and slice
your data efficiently.
```tsx
// components/pagination/slice.demo.tsx
'use client'
import { Group, Pagination, Text, usePaginationContext } from '@cerberus/react/react'
import { HStack, VStack } from 'styled-system/jsx'
import { users } from './users'
export function SliceDemo() {
return (
)
}
function UserList() {
const pagination = usePaginationContext()
return (
{pagination.slice(users).map((user) => (
{user.name}
{user.email}
))}
)
}
```
Checkout the [Pagination documentation for more details](/docs/components/pagination).
### `EnforceNoProperties` type
Sometimes you need a type that should strictly enforce the usage of some properties
and not others depending on the context. We do this often with our component APIs.
Thus, we have added a `EnforceNoProperties` type that allows you easily achieve this.
```typescript
pinning?: (boolean & EnforceNoProperties) | PinnedOptions
```
This type will convert the Record passed into it to have a values of `never`. The
result is a more strict and reliable type environment.
### Other Improvements
- Fix: React build no longer leaks internal dependencies
- Fix: React-related builds are overall smaller
- Fix: Some menus and buttons no longer trigger random `focus` outlines
- Fix: `Button` and `Text` autocomplete style props correctly
- Feat: Expose `MenuSelectionDetails` type
- Feat: Allow `ReactNode` descriptions for destructive Prompt modals
## Panda Preset
Our core preset plugin got a little boost as well with a new button recipe and
conditions.
### New Button Variant
Buttons now include a `outlined-subtle` variant that provides a less prominent
stroke style.
```tsx
// components/button/usage.demo.tsx
import { Button, ButtonProps, For } from '@cerberus/react/react'
import { HStack } from 'styled-system/jsx'
import recipesSpec from 'styled-system/specs/recipes.json'
const variants = (recipesSpec.data.find((r) => r.name === 'button')?.variants.usage ??
[]) as ButtonProps['usage'][]
export function UsageDemo() {
return (
{(usage) => (
)}
)
}
```
Checkout the [Button documentation for more details](/docs/components/button).
### New Conditions
Along with our new Data Grid API, we have added some new conditions to help with
conditional styling:
| Condition | Value |
| -------------- | -------------------------------------------- |
| `_pinned` | `'&:is([data-pinned], [data-state=pinned])'` |
| `_leftPinned` | `'&:is([data-pinned=left])'` |
| `_rightPinned` | `'&:is([data-pinned=right])'` |
## The Year of Cerberus
Cerberus has always aimed for the long-term goal of creating an entire platform
of high performant components and tools that help streamline web development.
In our first year, we focused on the foundation: _a plugin-based architecture that
worked seamlessly with React components._
This year we are building on that foundation to create a landmark powerhouse that
is **more than just a collection of components**.
We will achieve that by adding two more players in the ecosystem:
- **Compositional components**: Fully-baked feature packed React components
- **Signals**: An external Signal-based state management library for React
This release, introduces our first **compositional component**: The Data Grid
## Data Grid
The Data Grid is a React component built to provide a drop-in solution for displaying
data-heavy tables with features like filtering, sorting, pagination, and more. Our
execution is inspired by the ideaology of MUI's DataGrid component with the performance
enhancements of TanStack Table.
```tsx
// data-grid/quick-start/basic.demo.tsx
'use client'
import { DataGrid } from '@cerberus/react/data-grid'
import { useQuery } from '@cerberus/react/signals'
import { HStack } from 'styled-system/jsx'
import { queryEmployees } from '../api'
import { columns } from './columns.demo'
export function BasicDemo() {
const data = useQuery(queryEmployees(1000))
return (
)
}
```
The concept is simple:
1. Define your columns (and opt-in to features)
2. Pass your columns and data to the grid
**Supported Features:**
- Virtualization
- Sorting (and multi-column sorting)
- Pinning
- Pagination
- Access to state context
- Custom Toolbars
- Signal-driven state management for enhanced performance
**Dependencies:**
The Data Grid is a "pure" Cerberus component that only uses two dependencies:
- Cerberus React
- Cerberus Signals
Checkout the [Data Grid documentation](/docs/data-grid/overview) for more details.
#### Signals Early Preview
The Data Grid "soft launches" a new signal-based state management system for
React. This is the state management that React should have had from the beginning
_and still doesn't provide_.
We will dive deeper into signals in our _next release_ when we launch it publicly.
If you are interested in an early preview of signals, you can install it today:
```bash
npm install @cerberus/signals@npm:@cerberus-design/signals
```
## Thanks!
This is a monumental release and one of the most significant updates to Cerberus
since its inception.
A special thanks to everyone who has helped validate and shape the APIs and docs
for this release.
## Upgrading
To upgrade to this release, you can install the latest version of Cerberus React:
```bash
npm run up:cerberus
```
---
# Accordion
Category: Components
> A way to show and hide information by sections.
## Import
```ts
import { Accordion, AccordionItemGroup } from '@cerberus/react'
```
## Usage
The Accordion component is a controlled abstraction of the primitive components that can be used to show or hide content. It can be used in a group with other accordion items to allow for multiple selections.
```tsx
// components/accordion/basic.demo.tsx
import { Accordion, AccordionItemGroup, For, Show } from '@cerberus/react/react'
import Image from 'next/image'
import { Suspense } from 'react'
import { Box } from 'styled-system/jsx'
import data from './data.json'
export function BasicDemo() {
return (
{(item) => (
}>
{item.content}
)}
)
}
function FallbackContent() {
return (
)
}
```
## Indicator Position
The `AccordionItemGroup` component supports two indicator positions: `start` and `end`. This changes the position of the icon indicator displayed.
```tsx
// components/accordion/indicator.demo.tsx
import { Accordion, AccordionItemGroup, For, Show } from '@cerberus/react/react'
import { Box } from 'styled-system/jsx'
import data from './data.json'
export function IndicatorDemo() {
return (
{(item) => (
{item.content}
)}
)
}
```
## Sizes
The `Accordion` component supports two sizes: `sm` and `lg`. This changes the size of the icon indicator displayed.
```tsx
// components/accordion/size.demo.tsx
import { Accordion, AccordionItemGroup, For, Show } from '@cerberus/react/react'
import { Box } from 'styled-system/jsx'
import data from './data.json'
export function SizeDemo() {
return (
{(item) => (
{item.content}
)}
)
}
```
## Customizing
You can customize the Accordion using style props and data selectors.
```tsx
// components/accordion/custom.demo.tsx
import { AccordionParts } from '@cerberus/react/react'
import { Box } from 'styled-system/jsx'
export function CustomDemo() {
return (
🔥
Cerberus
Cerberus is the three-headed dog that guards the gates of the Underworld and
our sweet baby boi protecting the integrity of your design system.
)
}
```
## Primitives
The layers of the Accordion which can be used to create a fully custom solution.
| Component | Description |
| ---------------------- | ------------------------------------------------------------------ |
| AccordionRoot | The main container for the accordion items. |
| AccordionItem | Represents a single item within the accordion. |
| AccordionItemTrigger | The trigger element that toggles the visibility of the content. |
| AccordionItemContent | The content that is shown or hidden when the trigger is activated. |
| AccordionItemIndicator | An optional indicator to show the state of the accordion item. |
### Data Attributes
The primitives additionally use the following data attributes for custom styling:
| Name | Value | Description |
| ------------ | ---------------- | -------------------------------------- |
| `data-scope` | `accordion` | The scope of the components. |
| `data-part` | `root` | The root layer of the scope. |
| `data-part` | `item` | The item layer of the scope. |
| `data-part` | `item-trigger` | The item trigger layer of the scope. |
| `data-part` | `item-content` | The item content layer of the scope. |
| `data-part` | `item-indicator` | The item indicator layer of the scope. |
## API
### Accordion
The `Accordion` component is an abstraction of the primitives and accepts the following props:
| Name | Default | Description |
| ------ | ------- | ------------------------------- |
| `size` | `lg` | The size of the accordion item. |
The Accordion is an abstraction of the [Root component](https://ark-ui.com/react/docs/components/accordion#api-reference).
### AccordionItemGroup
The `AccordionItemGroup` component is an abstraction of the primitives and accepts the following props:
| Name | Default | Description |
| ------------------- | ------- | ------------------------------------------------ |
| `heading` | | The heading of the accordion item. |
| `value` | | The value of the accordion item. Must be unique. |
| `indicatorPosition` | `end` | The position of the indicator. |
The AccordionItemGroup is an abstraction of the [Item component](https://ark-ui.com/react/docs/components/accordion#api-reference).
### Parts
The `AccordionParts` API is an Object containing the full family of components.
> **Note:** It is best to only use the AccordionParts if you are building a custom solution. Importing Object based components will ship every property it includes into your bundle, regardless if you use it or not.
| Name | Description |
| --------------- | ------------------------------------------------------------------------------------------------------ |
| `Root` | The `AccordionRoot` component which is the Provider for the family. |
| `Item` | The `AccordionItem` component which displays the item. |
| `ItemTrigger` | The `AccordionItemTrigger` component which is the visual title that triggers the content to open. |
| `ItemContent` | The `AccordionItemContent` component which displays the content that is revealed from the ItemTrigger. |
| `ItemIndicator` | The `AccordionItemIndicator` component which displays the indicator (chevron) icon. |
---
# Admonition
Category: Components
> An abstracted informational component that can be used to display important messages or warnings to users.
## Import
```ts
import { Admonition } from '@cerberus/react'
```
## Usage
The `Admonition` component is used to display static messagesto users.
It can be used in various contexts, such as error, success, or informational messages.
```tsx
// components/admonition/basic.demo.tsx
import { Box } from '@/styled-system/jsx'
import { Admonition } from '@cerberus/react/react'
export function BasicDemo() {
return (
This is a note admonition that is commonly use to display a page-level
informational message.
)
}
```
## Palette
The `Admonition` component can be customized with different color palettes to
match the design of your application. You can use any of the state-related palettes
along with `page`.
```tsx
// components/admonition/palette.demo.tsx
import { Stack } from '@/styled-system/jsx'
import { Admonition, type AdmonitionProps, For } from '@cerberus/react/react'
import recipesSpec from 'styled-system/specs/recipes.json'
const variants = (recipesSpec.data.find((r) => r.name === 'admonition')?.variants
.palette ?? []) as AdmonitionProps['palette'][]
export function PaletteDemo() {
return (
{(palette) => (
This is a {palette} admonition.
)}
)
}
```
## Usage
Use the `usage` prop to display the admonition in a different style. The options
are `filled` (default) and `outlined`.
```tsx
// components/admonition/usage.demo.tsx
import { Stack } from '@/styled-system/jsx'
import { Admonition, type AdmonitionProps, For } from '@cerberus/react/react'
import recipesSpec from 'styled-system/specs/recipes.json'
const variants = (recipesSpec.data.find((r) => r.name === 'admonition')?.variants
.usage ?? []) as AdmonitionProps['usage'][]
export function UsageDemo() {
return (
{(usage) => (
This is a {usage} admonition.
)}
)
}
```
## Customizing
You can customize the Admonition any way you like with style props and data-selectors.
```tsx
// components/admonition/custom.demo.tsx
import { PawIcon } from '@/components/icons/paw'
import { Box, Circle } from '@/styled-system/jsx'
import { AdmonitionParts } from '@cerberus/react/react'
export function CustomDemo() {
return (
Cerberus Forever
President's are temporary, but Cerberus is forever.
)
}
```
## Primitives
You can utilize the primitive components or style props to customize the
admonition.
| Component | Description |
| --------------------- | --------------------------------------- |
| AdmonitionRoot | The container for the parts |
| AdmonitionIndicator | The icon of the admonition |
| AdmonitionContent | The content container of the admonition |
| AdmonitionHeading | The heading of the admonition |
| AdmonitionDescription | The description body of the admonition |
### Data Attributes
The primitives additionally use the following data attributes for custom styling:
| Name | Value | Description |
| ------------ | ------------- | ---------------------------- |
| `data-scope` | `admonition` | The scope of the components. |
| `data-part` | `root` | The part of the component. |
| `data-part` | `indicator` | The part of the component. |
| `data-part` | `content` | The part of the component. |
| `data-part` | `heading` | The part of the component. |
| `data-part` | `description` | The part of the component. |
## API
### Props
The `Admonition` component is an abstraction of the primitives and accepts the following props:
| Name | Default | Description |
| --------- | -------- | ----------------------------------------------------------- |
| `heading` | | The heading title of the Admonition. |
| `icon` | | An alternative icon to display in the Admonition. |
| `palette` | `page` | The color palette of the Admonition. |
| `usage` | `filled` | The usage of the Admonition. Can be `filled` or `outlined`. |
### Parts
The `AdmonitionParts` API is an Object containing the full family of components.
> **Note:** It is best to only use the AdmonitionParts if you are building a custom solution. Importing Object based components will ship every property it includes into your bundle, regardless if you use it or not.
| Name | Description |
| ------------- | --------------------------------------------------------------------------- |
| `Root` | The `AdmonitionRoot` component which is the container of the family. |
| `Indicator` | The `AdmonitionIndicator` component which is the icon of the admonition. |
| `Content` | The `AdmonitionContent` component which is the container of the admonition. |
| `Heading` | The `AdmonitionHeading` component which is the heading of the admonition. |
| `Description` | The `Admonition` component which is the description body of the admonition. |
---
# Aspect Ratio
Category: Layout
> Used to embed responsive videos and maps, etc.
## Import
```ts
import { AspectRatio } from 'styled-system/jsx'
```
## Usage
```tsx
// components/aspect-ratio/basic.demo.tsx
import { AspectRatio } from 'styled-system/jsx'
export function BasicDemo() {
return (
)
}
```
### Image
Here's how to embed an image that has a 4 by 3 aspect ratio.
```tsx
// components/aspect-ratio/img.demo.tsx
import Image from 'next/image'
import { AspectRatio } from 'styled-system/jsx'
export function ImgDemo() {
return (
)
}
```
### Video
To embed a video with a specific aspect ratio, use an iframe with `src` pointing
to the link of the video.
> *Example code for components/aspect-ratio/video is missing or could not be loaded.*
### Google Map
Here's how to embed a responsive Google map using `AspectRatio`.
```tsx
// components/aspect-ratio/maps.demo.tsx
import { AspectRatio } from 'styled-system/jsx'
export function MapsDemo() {
return (
)
}
```
### Responsive
Here's an example of applying a responsive aspect ratio to a box.
```tsx
// components/aspect-ratio/responsive.demo.tsx
import { DecorativeBox } from '@/components/decorative-box'
import { AspectRatio } from 'styled-system/jsx'
export function ResponsiveDemo() {
return (
Box
)
}
```
## Props
These props can be passed to the `AspectRatio` component in addition to style and factory props.
| Prop | Type | Description |
| ------- | -------------------------- | --------------------------------------------------------------------------------------- |
| `ratio` | `ConditionalValue` | The aspect ratio of the Box. Common values are: `21/9`, `16/9`, `9/16`, `4/3`, `1.85/1` |
---
# Avatar
Category: Components
> The Avatar component is used to represent user profile picture or initials.
## Import
```ts
import { Avatar } from '@cerberus/react'
```
## Usage
The `Avatar` component is used to represent a user or entity. It can be used to display a user's profile picture, initials, or a generic icon.
> **Note:** The avatar uses a smart hierarchy method. If a source is provided and loads successfully - it will be shown. In all other cases, the fallback will be shown.
```tsx
// components/avatar/basic.demo.tsx
import { UserAvatar } from '@carbon/icons-react'
import { Avatar } from '@cerberus/react/react'
import { HStack } from 'styled-system/jsx'
export function BasicDemo() {
return (
} />
)
}
```
## Sizes
You can change the size of an Avatar with the `size` prop.
```tsx
// components/avatar/sizes.demo.tsx
import { Avatar, AvatarProps, For } from '@cerberus/react/react'
import { HStack } from 'styled-system/jsx'
import recipesSpec from 'styled-system/specs/recipes.json'
const variants = (recipesSpec.data.find((r) => r.name === 'avatar')?.variants.size ??
[]) as AvatarProps['size'][]
export function SizesDemo() {
return (
{(size) => (
)}
)
}
```
## Gradients
Use the `gradient` prop to use any [gradient](/docs/theming/gradients) for the background color.
```tsx
// components/avatar/gradient.demo.tsx
import { UserAvatar } from '@carbon/icons-react'
import { Avatar, For, Text } from '@cerberus/react/react'
import { GRADIENTS } from '@cerberus/tokens/src/const'
import { Grid, GridItem, HStack } from 'styled-system/jsx'
export function GradientDemo() {
return (
{(gradient) => (
} gradient={gradient} />
{gradient}
)}
)
}
```
## Group
Use the `Group` component to stack Avatars together.
```tsx
// components/avatar/group.demo.tsx
import { UserAvatar } from '@carbon/icons-react'
import { Avatar, Group } from '@cerberus/react/react'
export function GroupDemo() {
return (
} />
)
}
```
## Badge
Use the `Float` component to show badges.
```tsx
// components/avatar/float.demo.tsx
import { UserAvatar } from '@carbon/icons-react'
import { Avatar } from '@cerberus/react/react'
import { Box, Circle, Float, HStack } from 'styled-system/jsx'
export function FloatDemo() {
return (
} />
)
}
```
## `asChild` Prop
You can use the `asChild` prop to render the `Avatar` component as a different element. Cerberus will pass all the props onto your child.
This is great for when you need to link the avatar to a profile page.
```tsx
// components/avatar/next.demo.tsx
import { css } from '@/styled-system/css'
import { AvatarRoot } from '@cerberus/react/react'
import Image from 'next/image'
import Link from 'next/link'
export function NextDemo() {
return (
)
}
```
## Customization
You can customize the Avatar any way you like with style props and data-selectors.
```tsx
// components/avatar/custom.demo.tsx
import { Avatar } from '@cerberus/react/react'
export function CustomAvatar() {
return (
)
}
```
## Primitives
The layers of the Avatar which can be used to create a fully custom solution.
| Component | Description |
| ---------------- | ---------------------------------------------- |
| `AvatarRoot` | The context provider for the Avatar parts |
| `AvatarImage` | The img of the avatar |
| `AvatarFallback` | The fallback content to display for the avatar |
### Data Attributes
The primitives additionally use the following data attributes for custom styling:
| Name | Value | Description |
| ------------ | ---------- | -------------------------------- |
| `data-scope` | `avatar` | The scope of the components. |
| `data-part` | `root` | The root layer of the scope. |
| `data-part` | `fallback` | The fallback layer of the scope. |
| `data-part` | `image` | The image layer of the scope. |
## API
### Props
The `Avatar` component is an abstraction of the primitives and accepts the following props:
| Name | Default | Description |
| ---------- | ------- | ------------------------------------------------- |
| `fallback` | | The fallback content to display for the avatar. |
| `src` | | The `src` of the avatar. |
| `alt` | | The `alt` of the avatar. Required if using `src`. |
| `gradient` | `none` | The gradient theme to display |
| `size` | `md` | This size of the avatar. |
The `Avatar` component also accepts all the props of the [`AvatarRoot` primitive props](https://ark-ui.com/react/docs/components/avatar#api-reference)
### Parts
The `AvatarParts` API is an Object containing the full family of components.
> **Note:** It is best to only use the AvatarParts if you are building a custom solution. Importing Object based components will ship every property it includes into your bundle, regardless if you use it or not.
| Name | Description |
| ---------- | --------------------------------------------------------------------------------------------------------- |
| `Root` | The `AvatarRoot` component which is the Provider for the family. |
| `Image` | The `AvatarImage` component which displays the image. |
| `Fallback` | The `AvatarFallback` component which is the content to show when there is no image (or it fails to load). |
---
# Bleed
Category: Layout
> Used to break an element from the boundaries of its container
## Import
```ts
import { Bleed } from 'styled-system/jsx'
```
## Usage
The Bleed pattern is used to create a full width element by negating the padding
applied to its parent container.
```tsx
// components/bleed/basic.demo.tsx
import { Bleed, Box, VStack } from 'styled-system/jsx'
import { Text } from '@cerberus/react/react'
import { DecorativeBox } from '@/components/decorative-box'
export function BasicDemo() {
return (
Bleed
Some Heading
This is some descriptive content of the heading.
)
}
```
### Vertical
Use the `block` prop to make the element bleed vertically.
```tsx
// components/bleed/vertical.demo.tsx
import { DecorativeBox } from '@/components/decorative-box'
import { Bleed, Box } from 'styled-system/jsx'
export function VerticalDemo() {
return (
Bleed
)
}
```
### Specific Direction
Use the `inline` and `block` props to make the element bleed in a specific direction.
```tsx
// components/bleed/direction.demo.tsx
import { DecorativeBox } from '@/components/decorative-box'
import { Bleed, Box, VStack } from 'styled-system/jsx'
export function DirectionDemo() {
return (
inlineblock
)
}
```
## Props
The `Bleed` component accepts the following utility props in addition to style and factory props:
| Prop | Type | Description |
| -------- | ----------------------------------- | ---------------------------------- |
| `inline` | `SystemStyleObject['marginInline']` | The negative margin on the x-axis. |
| `block` | `SystemStyleObject['marginBlock']` | The negative margin on the y-axis. |
---
# Box
Category: Layout
> The most abstract styling component in Cerberus on top of which all other Cerberus components are built.
## Import
```ts
import { Box } from 'styled-system/jsx'
```
## Usage
The Box pattern does not contain any additional styles. With its function form
it's the equivalent of the `css` function. It can be useful with its JSX form and
is the equivalent of a `styled.div` component, serving mostly to get style props
available in JSX.
```tsx
// components/box/basic.demo.tsx
import { Box } from 'styled-system/jsx'
export function BasicDemo() {
return (
This is the Box
)
}
```
### Shorthand
Use shorthand like `bgColor` instead of `backgroundColor`, `m` instead of `margin`, etc.
```tsx
// components/box/shorthand.demo.tsx
import { Box } from 'styled-system/jsx'
export function ShorthandDemo() {
return (
This is the Box
)
}
```
### Pseudo Props
Use pseudo props like `_hover` to apply styles on hover, `_focus` to apply
styles on focus, etc.
```tsx
// components/box/pseudo.demo.tsx
import { Box } from 'styled-system/jsx'
export function PsuedoDemo() {
return (
A Box is not a Button
)
}
```
### Border
Use the `border` and `borderColor` prop to apply border styles.
```tsx
// components/box/border.demo.tsx
import { Box } from 'styled-system/jsx'
export function BorderDemo() {
return (
Box with a border
)
}
```
### Shadow
Use the `boxShadow` or `shadow` prop to apply shadow styles.
```tsx
// components/box/shadow.demo.tsx
import { Box } from 'styled-system/jsx'
export function ShadowDemo() {
return (
This is the Box with shadow
)
}
```
## Props
The `Box` component supports all style or factory props for styling.
---
# Button
Category: Components
> Used to trigger actions or events.
## Import
```ts
import { Button } from '@cerberus/react'
```
## Basic Usage
```tsx
// components/button/basic.demo.tsx
import { Button } from '@cerberus/react/react'
export function BasicDemo() {
return
}
```
### With Icon
To display an icon, use the `ButtonParts` API.
```tsx
// components/button/icon.demo.tsx
'use client'
import { Button } from '@cerberus/react/react'
import { ArrowDownRight } from '@carbon/icons-react'
export function IconDemo() {
return (
)
}
```
> **Note:** When using the pending prop, the icon will be replaced with a spinner.
## Pending
To display a loading state, use the `ButtonParts.Icon` and set the `pending` prop to `true`.
```tsx
// components/button/pending.demo.tsx
'use client'
import { Button } from '@cerberus/react/react'
export function PendingDemo() {
return (
)
}
```
## Shapes
Use the `shape` prop to set the shape of the button. The `Button` component comes in three shapes: `default`, `sharp` and `rounded`.
```tsx
// components/button/shapes.demo.tsx
import { Button, ButtonProps, For } from '@cerberus/react/react'
import { HStack } from 'styled-system/jsx'
import recipesSpec from 'styled-system/specs/recipes.json'
const variants = (recipesSpec.data.find((r) => r.name === 'button')?.variants.shape ??
[]) as ButtonProps['shape'][]
export function ShapesDemo() {
return (
{(shape) => (
)}
)
}
```
## Usage
Use the `usage` prop to set the usage style of the button. The `Button` component comes in three usage styles: `filled`, `outlined`, and `ghost`.
```tsx
// components/button/usage.demo.tsx
import { Button, ButtonProps, For } from '@cerberus/react/react'
import { HStack } from 'styled-system/jsx'
import recipesSpec from 'styled-system/specs/recipes.json'
const variants = (recipesSpec.data.find((r) => r.name === 'button')?.variants.usage ??
[]) as ButtonProps['usage'][]
export function UsageDemo() {
return (
{(usage) => (
)}
)
}
```
## Sizes
Use the `size` prop to change the Button size. The options are `xs` up to `2xl`.
```tsx
// components/button/size.demo.tsx
import { Button, ButtonProps, For } from '@cerberus/react/react'
import { HStack } from 'styled-system/jsx'
import recipesSpec from 'styled-system/specs/recipes.json'
const sizes = (recipesSpec.data.find((r) => r.name === 'button')?.variants.size ??
[]) as ButtonProps['size'][]
export function SizeDemo() {
return (
{(size) => (
)}
)
}
```
## Group
You can group buttons together using the `ButtonGroup` component.
```tsx
// components/button/group.demo.tsx
import { Button, ButtonGroup } from '@cerberus/react/react'
export function GroupDemo() {
return (
)
}
```
### Attached Button Group
You can create an attached button group by setting the `layout` prop to `attached`.
```tsx
// components/button/attached.demo.tsx
import { Button, ButtonGroup, IconButton } from '@cerberus/react/react'
import { ChevronDown } from '@carbon/icons-react'
import { HStack } from 'styled-system/jsx'
export function AttachedDemo() {
return (
)
}
```
## Primitives
You can utilize the primitive components or the `css` prop to customize the button.
| Component | Description |
| ------------- | ----------------------------------------- |
| `Button.Root` | The context provider for the Button parts |
| `Button.Icon` | The icon/spinner of the button |
```tsx
// components/button/custom.demo.tsx
import { Button } from '@cerberus/react/react'
export function CustomDemo() {
return (
)
}
```
## Global Customization
To enable global customization, you can modify the `theme` configuration in your `panda.config.ts` file. For example, to customize the button component globally:
```tsx
// components/button/global.demo.tsx
import { createCerberusConfig } from '@cerberus/panda-preset'
export default createCerberusConfig({
// ...
theme: {
extend: {
recipes: {
button: {
defaultVariants: {
shape: 'default',
},
},
},
},
},
})
```
This change will set the default shape variant of all buttons to `default`.
## API
### Props
The `Button` component is an abstraction of the primitives and accepts the following props:
| Name | Default | Description |
| --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `palette` | `action` | The color palette (any of the Cerberus palettes) of the button. |
| `usage` | `filled` | The style treatment of the button. |
| `shape` | `sharp` | The shape of the button. |
| `size` | `lg` | The size of the Button. |
| `pending` | `false` | The loading state of the button. When a button has an `Icon` it will replace it with a spinner. When not, is shows a disabled state. |
| `asChild` | | Render the Button as the child component. |
#### ButtonGroup Props
| Name | Default | Description |
| -------- | --------- | ----------------------------------------------------------------------------------------------------- |
| `layout` | `default` | The layout of the button group. Can be `default` or `attached`. |
| `shape` | `sharp` | The shape of the buttons in the group. Should match the `shape` prop of the `Button` components used. |
---
# Carousel
Category: Components
> Used to cycle through a series of visual content within a container.
## Import
```ts
import { Carousel } from '@cerberus/react'
```
## Usage
```tsx
// components/carousel/basic.demo.tsx
import { Center } from '@/styled-system/jsx'
import { Button, Carousel, For } from '@cerberus/react/react'
import { ITEMS } from './items'
export function BasicDemo() {
return (
{(item, idx) => (
{item.content}
)}
)
}
```
## With Indicators
Use the `showIndicators` prop to display indicators for each slide.
```tsx
// components/carousel/indicator.demo.tsx
import { Center } from '@/styled-system/jsx'
import { Carousel, For } from '@cerberus/react/react'
import { ITEMS } from './items'
export function IndicatorDemo() {
return (
{(item, idx) => (
{item.content}
)}
)
}
```
## Primitives
You can utilize the primitive components or the `css` prop to customize the button.
| Component | Description |
| ------------------------ | ------------------------------------------------ |
| `CarouselRoot` | The context provider for the Carousel parts |
| `CarouselControl` | The container of the Next and Prev triggers |
| `CarouselPrevTrigger` | The button to navigate to the previous slide |
| `CarouselNextTrigger` | The button to navigate to the next slide |
| `CarouselItemGroup` | The container for the Carousel items |
| `CarouselItem` | The individual item within the Carousel |
| `CarouselIndicatorGroup` | The container for the indicators |
| `CarouselIndicator` | The individual indicator representing each slide |
### Data Attributes
The primitives additionally use the following data attributes for custom styling:
| Attribute | Value |
| -------------------- | ------------------------------- |
| `[data-scope]` | carousel |
| `[data-part]` | root |
| `[data-orientation]` | The orientation of the carousel |
### Root CSS Variables
The Root primitive sets the following CSS variables for customization:
| Variable | Description |
| ------------------- | -------------------------------------- |
| `--slides-per-page` | The number of slides visible per page |
| `--slide-spacing` | The spacing between slides |
| `--slide-item-size` | The calculated size of each slide item |
## API
### Props
The `Carousel` component is an abstraction of the primitives and accepts the following props:
| Name | Default | Description |
| ---------------- | ------- | ------------------------------------------ |
| `showIndicators` | `false` | Whether to show indicators for each slide. |
The Carousel also accepts all the props of the [Root component](https://ark-ui.com/docs/components/carousel#api-reference).
### Parts
The `CarouselParts` API is an Object containing the full family of components.
> **Note:** It is best to only use the CarouselParts if you are building a custom solution. Importing Object based components will ship every property it includes into your bundle, regardless if you use it or not.
| Name | Description |
| ---------------- | ------------------------------------------------------------------------------------- |
| `Root` | The `Carousel` component which is the Provider for the family. |
| `Control` | The container of the Next and Prev triggers. |
| `PrevTrigger` | The button to navigate to the previous slide. |
| `NextTrigger` | The button to navigate to the next slide. |
| `ItemGroup` | The container for the Carousel items. |
| `Item` | The `CarouselItem` component which represents an individual item within the carousel. |
| `IndicatorGroup` | The container for the indicators. |
| `Indicator` | The individual indicator representing each slide. |
---
# Center
Category: Layout
> Used to center its child within itself.
## Import
```ts
import { Center } from 'styled-system/jsx'
```
## Usage
The Center pattern is used to center the content of a container.
```tsx
// components/center/basic.demo.tsx
import { DecorativeBox } from '@/components/decorative-box'
import { Center } from 'styled-system/jsx'
export function BasicDemo() {
return (
This is centered
)
}
```
### Icon
Center can be used to create frames around icons or numbers.
```tsx
// components/center/icon.demo.tsx
import { Center, HStack } from 'styled-system/jsx'
import { Text } from '@cerberus/react/react'
import { Phone } from '@carbon/icons-react'
export function IconDemo() {
return (
1
)
}
```
### Center with Inline
Use the `inline` to change the display to `inline-flex`.
```tsx
// components/center/inline.demo.tsx
import { Center } from 'styled-system/jsx'
import { Text } from '@cerberus/react/react'
import { Phone } from '@carbon/icons-react'
export function InlineDemo() {
return (
This will be centered
)
}
```
### Square
`Square` centers its child given the `size` (width and height).
```tsx
// components/center/square.demo.tsx
import { PhoneIncoming } from '@carbon/icons-react'
import { Square } from 'styled-system/jsx'
export function SquareDemo() {
return (
)
}
```
### Circle
`Circle` centers its child given the `size` and creates a circle around it.
```tsx
// components/center/circle.demo.tsx
import { PhoneBlock } from '@carbon/icons-react'
import { Circle } from 'styled-system/jsx'
export function CircleDemo() {
return (
)
}
```
## Props
The `Center` component accepts the following utility props in addition to style and factory props:
| Prop | Type | Description |
| -------- | --------- | ----------------------------------------- |
| `inline` | `boolean` | Changes the display to `inline-flex`. |
| `size` | `string` | Sets the width and height of the element. |
---
# Cerberus Context
Category: Concepts
> Register custom options for interal component usage.
## Import
```ts
import { CerberusProvider, makeSystemConfig, defineIcons } from '@cerberus/react'
```
## Usage
The Cerberus Context provides the ability to register custom options for internal
component usage.
```tsx
'use client'
import { CerberusProvider } from '@cerberus-design/react'
import { type PropsWithChildren } from 'react'
export function CerberusConfig(props: PropsWithChildren) {
return {props.children}
}
```
## Config
The Cerberus Context configuration is passed to the `config` property of the `CerberusProvider`
via the `makeSystemConfig` helper.
```tsx
'use client'
import { CerberusProvider, makeSystemConfig } from '@cerberus-design/react'
import { PropsWithChildren } from 'react'
const config = makeSystemConfig()
export function ConfigDemo(props: PropsWithChildren) {
return {props.children}
}
```
## Registering Custom Icons
To register a different set of icons for your application, you can use the
`defineIcons` function and pass it to the `icons` prop of the `CerberusProvider`
`config` property.
```tsx
'use client'
import { CerberusProvider, defineIcons, makeSystemConfig } from '@cerberus-design/react'
import { ChevronDown } from '@carbon/icons-react'
import type { PropsWithChildren } from 'react'
const icons = defineIcons({
// Add a different icon for the accordion indicator
accordionIndicator: ChevronDown,
})
const config = makeSystemConfig({
icons,
})
export default function CerberusConfig(props: PropsWithChildren<{}>) {
return {props.children}
}
```
### Icon overrides
Here is the exhaustive list of icon overrides you can configure:
| Name | Description |
| --------------------- | --------------------------------------------------------------------------------- |
| `accordionIndicator` | The arrow indicator used in accordion components. |
| `arrowRight` | The arrow indicator used for representing moving forward. |
| `avatar` | The fallback icon used when no avatar is available. |
| `calendar` | The calendar icon used in date picker components. |
| `calendarPrev` | The prev month trigger icon used in date picker components. |
| `calendarNext` | The next month trigger icon used in date picker components. |
| `caretDown` | The caret down icon used in dropdown components. |
| `checkbox` | The checkbox icon used in checkbox components. |
| `close` | The close icon used in closeable components. |
| `column` | The column icon used in column-related components. |
| `confirmModal` | The header icon used in confirm modals. |
| `decrement` | The icon used for decrement triggers. |
| `delete` | The icon used for delete triggers. |
| `dangerNotification` | The icon used for danger notifications. |
| `error` | The icon used for error notifications. |
| `fileUploader` | The icon used for file uploader components. |
| `filter` | The icons used to represent filtering things. |
| `filterEdit` | The icons used to represent editing filtered things. |
| `filterClear` | The icons used to represent clearing filters. |
| `indeterminate` | The icon used for indeterminate states. |
| `increment` | The icon used for increment triggers. |
| `info` | The icon used for info triggers. |
| `infoNotification` | The icon used for info notifications. |
| `invalid` | The icon used for invalid states. |
| `invalidAlt` | The icon used for alternative invalid states. |
| `moreVertical` | The icon used for more options triggers. |
| `pinLeft` | The icon used for pin left triggers. |
| `pinLeftFilled` | The icon used for pin left triggers with a filled style. |
| `pinRight` | The icon used for pin right triggers. |
| `pinRightFilled` | The icon used for pin right triggers with a filled style. |
| `promptModal` | The icon used for prompt modal triggers. |
| `redo` | The icon used for redo triggers. |
| `search` | The icon used for search triggers. |
| `selectArrow` | The icon used for select menu triggers. |
| `selectChecked` | The icon used for the checked state of a select menu item. |
| `sortAsc` | The icon used for ascending sort triggers. |
| `sortDesc` | The icon used for descending sort triggers. |
| `success` | The icon used for success triggers. |
| `successNotification` | The icon used for success notifications. |
| `table` | An icon used to represent tables. |
| `toggleChecked` | The icon used for toggle checked triggers. |
| `viewOff` | The icon used for view off triggers (e.g., hide items, password visibility, etc.) |
| `waitingFileUploader` | The icon used for a loading state in the File Uploader. |
| `warningNotification` | The icon used for warning notifications. |
| `warning` | The icon used for warning triggers. |
## Config Options
| Name | Description |
| ------- | ------------------------------------------------- |
| `icons` | Custom icons to register for all component usage. |
---
# Checkbox
Category: Components
> The Checkbox component allows users to select one or more options from a set.
## Import
```ts
import { Checkbox, CheckboxGroup } from '@cerberus/react'
```
## Usage
The Checkbox component is a controlled component that can be used to select one or more options from a set. It can be used in a group with other checkboxes to allow for multiple selections.
```tsx
// components/checkbox/basic.demo.tsx
'use client'
import { Box } from '@/styled-system/jsx'
import { Checkbox, CheckboxGroup, For } from '@cerberus/react/react'
const items = [
{
id: 'terms',
label: 'I agree to the terms and conditions',
required: true,
},
{
id: 'legal',
label: 'I would like to receive marketing emails',
},
{
id: 'disabled',
label: 'This is a disabled checkbox',
disabled: true,
checked: true,
},
]
export function BasicDemo() {
return (
{(item) => (
{item.label}
)}
The indeterminate state
)
}
```
## Indeterminate State
The Checkbox component supports the indeterminate state, which is useful for indicating that a group of checkboxes is in a mixed state. To enable this state, set the `checked` prop to `indeterminate`.
```tsx
// components/checkbox/indeterminate.demo.tsx
'use client'
import { Checkbox, CheckboxGroup, For } from '@cerberus/react/react'
import { createComputed, useRead, useSignal } from '@cerberus/react/signals'
import { Box } from 'styled-system/jsx'
const initialValues = [
{ label: 'Monday', checked: false, value: 'monday' },
{ label: 'Tuesday', checked: false, value: 'tuesday' },
{ label: 'Wednesday', checked: false, value: 'wednesday' },
{ label: 'Thursday', checked: false, value: 'thursday' },
]
export function IndeterminateDemo() {
const [values, setValues, getValues] = useSignal(initialValues)
const allChecked = createComputed(() => getValues().every((value) => value.checked))
const indeterminate = createComputed(
() => getValues().some((value) => value.checked) && !allChecked(),
)
const checkedState = createComputed<'indeterminate' | boolean>(() => {
return indeterminate() ? 'indeterminate' : allChecked()
})
const checked = useRead(checkedState)
return (
{
setValues((current) =>
current.map((value) => ({ ...value, checked: !!e.checked })),
)
}}
>
Weekdays
{(item, index) => (
{
setValues((current) => {
const newValues = [...current]
newValues[index] = { ...newValues[index], checked: !!e.checked }
return newValues
})
}}
>
{item.label}
)}
)
}
```
## Sizes
The Checkbox component supports different sizes. You can set the size prop to `sm`, or `md`.
```tsx
// components/checkbox/size.demo.tsx
'use client'
import { Box } from '@/styled-system/jsx'
import { Checkbox, CheckboxGroup, CheckboxProps, For } from '@cerberus/react/react'
import recipesSpec from 'styled-system/specs/recipes.json'
const variants = (recipesSpec.data.find((r) => r.name === 'checkbox')?.variants.size ??
[]) as CheckboxProps['size'][]
export function SizeDemo() {
return (
{(size) => (
{String(size)}
)}
)
}
```
## Customizing
You can customize the Checkbox any way you like with style props and data-selectors.
```tsx
// components/checkbox/custom.demo.tsx
'use client'
import { Box } from '@/styled-system/jsx'
import { ArrowDownRight } from '@carbon/icons-react'
import { CheckboxParts } from '@cerberus/react/react'
export function CustomDemo() {
return (
Custom Checkbox
)
}
```
## Primitives
The layers of the Checkbox which can be used to create a fully custom solution.
| Component | Description |
| --------------------- | ------------------------------------------- |
| `CheckboxRoot` | The context provider for the Checkbox parts |
| `CheckboxLabel` | The label of the checkbox |
| `CheckboxControl` | The input of the checkbox |
| `CheckboxIndicator` | The indicator of the checkbox state |
| `CheckboxGroup` | The group container for multiple checkboxes |
| `CheckboxHiddenInput` | The native input for the checkbox |
### Data Attributes
The primitives additionally use the following data attributes for custom styling:
| Name | Value | Description |
| -------------------- | ---------------------------------------------- | --------------------------------- |
| `data-scope` | `checkbox` | The scope of the components. |
| `data-part` | `root` | The root layer of the scope. |
| `data-part` | `label` | The label layer of the scope. |
| `data-part` | `control` | The control layer of the scope. |
| `data-part` | `indicator` | The indicator layer of the scope. |
| `data-part` | `group` | The group layer of the scope. |
| `data-active` | Present when active or pressed |
| `data-focus` | Present when focused |
| `data-focus-visible` | Present when focused with keyboard |
| `data-readonly` | Present when read-only |
| `data-hover` | Present when hovered |
| `data-disabled` | Present when disabled |
| `data-state` | `"indeterminate" \| "checked" \| "unchecked"` |
| `data-invalid` | Present when invalid |
| `data-required` | Present when required |
## API
### Props
The `Checkbox` component is an abstraction of the primitives and accepts the following props:
| Prop | Type | Required | Description |
| --------------------------- | -------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `size` | `sm` | No | This size of the Checkbox. |
| `checked` | `CheckedState` | No | The controlled checked state of the checkbox |
| `defaultChecked` | `CheckedState` | No | The initial checked state of the checkbox when rendered. Use when you don't need to control the checked state of the checkbox. |
| `disabled` | `boolean` | No | Whether the checkbox is disabled |
| `form` | `string` | No | The id of the form that the checkbox belongs to. |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{ root: string; hiddenInput: string; control: string; label: string }>` | No | The ids of the elements in the checkbox. Useful for composition. |
| `invalid` | `boolean` | No | Whether the checkbox is invalid |
| `name` | `string` | No | The name of the input field in a checkbox. |
| Useful for form submission. |
| `onCheckedChange` | `(details: CheckedChangeDetails) => void` | No | The callback invoked when the checked state changes. |
| `readOnly` | `boolean` | No | Whether the checkbox is read-only |
| `required` | `boolean` | No | Whether the checkbox is required |
| `value` | `string` | No | The value of checkbox input. Useful for form submission. |
### Parts
The `CheckboxParts` API is an Object containing the full family of components.
> **Note:** It is best to only use the CheckboxParts if you are building a custom solution. Importing Object based components will ship every property it includes into your bundle, regardless if you use it or not.
| Name | Description |
| ------------- | ------------------------------------------------------------------------------ |
| `Root` | The `CheckboxRoot` component which is the Provider for the family. |
| `Label` | The `CheckboxLabel` component which displays the label and "required" notice. |
| `Control` | The `CheckboxControl` component which is the visual field. |
| `Indicator` | The `CheckboxIndicator` component which displays based on the `checked` state. |
| `Group` | The `CheckboxGroup` component which creates a controls the spacing of a group. |
| `HiddenInput` | The `CheckboxHiddenInput` component which displays the native input. |
---
# Client Only
Category: Utilities
> Renders children only on the client side.
## Motivation
The `ClientOnly` component renders its children only on the client side. This is useful for components that need to
access the DOM or browser APIs that are not available on the server side.
## Examples
### Basic
```tsx
// components/client-only/basic.demo.tsx
import { ClientOnly } from '@cerberus/react/react'
// Notice this module is NOT using the 'use-client' directive
export function BasicDemo() {
return (
This content is only rendered on the client side.
)
}
```
### With Fallback
To render someting on the server before rendering on the client, use the `fallback` prop.
```tsx
// components/client-only/fallback.demo.tsx
import { ClientOnly } from '@cerberus/react/react'
// Notice this module is NOT using the 'use-client' directive
export function FallbackDemo() {
return (
Loading on the server...}>
This content is only rendered on the client side.
)
}
```
## API
### Props
| Prop | Type | Required | Default | Description |
| -------------- | ----- | -------- | ----------- | ----------- |
| **`fallback`** | `any` | false | `undefined` | undefined |
---
# Clipboard
Category: Components
> Used to copy text to the clipboard.
## Import
```ts
import { Clipboard } from '@cerberus/react'
```
## Usage
The Clipboard component provides an accessible way to copy text to the user's clipboard with built-in visual feedback.
The Clipboard component is **unstyled by default**, allowing for full customization.
```tsx
// components/clipboard/basic.demo.tsx
'use client'
import { HStack } from '@/styled-system/jsx'
import { Clipboard, IconButton, Text } from '@cerberus/react/react'
export function BasicDemo() {
return (
Hello, World!
)
}
```
## Button
Use the `asChild` prop on the `Clipboard.Trigger` to create a copy button.
```tsx
// components/clipboard/button.demo.tsx
'use client'
import { Button, Clipboard } from '@cerberus/react/react'
// [!code word:asChild]
export function ButtonDemo() {
return (
)
}
```
## Input
Use the `asChild` or style props prop on the `Clipboard.Input` to create a copy input.
> **Note:** A Button is valid as a trigger replacement because the Clipboard trigger will pass the aria attributes to the button.
```tsx
// components/clipboard/input.demo.tsx
'use client'
import { Button, Clipboard, Group } from '@cerberus/react/react'
export function InputDemo() {
return (
)
}
```
## Store
Use the `useClipboard` hook to create a fully custom solution.
```tsx
// components/clipboard/store.demo.tsx
'use client'
import { Button, Show, useClipboard } from '@cerberus/react/react'
export function StoreDemo() {
const clipboard = useClipboard({ value: 'https://cerberus.digitalu.design/' })
return (
)
}
```
## Primitives
You can utilize the primitive components or the `css` prop to customize the clipboard component.
| Component | Description |
| -------------------- | --------------------------------------------------- |
| `ClipboardRoot` | The root context provider for the clipboard family. |
| `ClipboardLabel` | A label element for accessibility. |
| `ClipboardControl` | The wrapper for input and trigger elements. |
| `ClipboardInput` | The input element containing the text to copy. |
| `ClipboardTrigger` | The button that triggers the copy action. |
| `ClipboardIndicator` | Shows the current state (copy/copied) with icons. |
| `CopyText` | Shows the current state (copy/copied) with text. |
### Data Attributes
The Clipboard component exposes several data attributes for better accessibility and customization.
| Name | Value | Description |
| ------------- | --------------------------------- | ---------------------------- |
| `data-scope` | `clipboard` | The scope of the components. |
| `data-part` | `root` | The root container part. |
| `data-part` | `label` | The label part. |
| `data-part` | `control` | The control part. |
| `data-part` | `input` | The input part. |
| `data-part` | `trigger` | The trigger part. |
| `data-part` | `indicator` | The indicator part. |
| `data-copied` | Present when copied state is true |
## API
### Root
| Prop | Type | Required | Description |
| ---------------- | --------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `defaultValue` | `string` | No | The initial value to be copied to the clipboard when rendered. Use when you don't need to control the value of the clipboard. |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{ root: string; input: string; label: string }>` | No | The ids of the elements in the clipboard. Useful for composition. |
| `onStatusChange` | `(details: CopyStatusDetails) => void` | No | The function to be called when the value is copied to the clipboard |
| `onValueChange` | `(details: ValueChangeDetails) => void` | No | The function to be called when the value changes |
| `timeout` | `number` | No | The timeout for the copy operation |
| `value` | `string` | No | The controlled value of the clipboard |
The Clipboard.Root is an abstraction of the [Root component](https://ark-ui.com/react/docs/components/clipboard#api-reference).
### Indicator
The indicator accepts the following props:
| Name | Default | Description |
| ------ | ------- | ------------------------------------------------------------------------ |
| `size` | `16` | The size of the indicator (in pixels). Can be `16`, `20`, `24`, or `32`. |
### Parts
The `Clipboard` API is an Object containing the full family of components.
> **Note:** It is best to only use the Clipboard parts if you are building a custom solution. Importing Object based components will ship every property it includes into your bundle, regardless if you use it or not.
| Name | Description |
| ----------- | ----------------------------------------------------------------------------- |
| `Root` | The `ClipboardRoot` component which is the Provider for the family. |
| `Label` | The `ClipboardLabel` component for accessibility. |
| `Control` | The `ClipboardControl` component wrapper. |
| `Input` | The `ClipboardInput` component displaying the text. |
| `Trigger` | The `ClipboardTrigger` component button. |
| `Indicator` | The `CopyIndicator` component with default copy/checkmark icons. |
| `CopyText` | The `CopyText` component with default "Copy"/"Copied" text indicators. |
| `Context` | The raw context for accessing clipboard state within the component hierarchy. |
---
# Collapsible
Category: Components
> Used to expand and collapse additional content.
## Import
```ts
import { Collapsible } from '@cerberus/react'
```
## Usage
The Collapsible component allows you to show or hide content with a trigger. It is useful for accordions, dropdowns, and expandable sections.
```tsx
// components/collapsible/basic.demo.tsx
'use client'
import { Box, HStack } from '@/styled-system/jsx'
import { ChevronDown } from '@carbon/icons-react'
import { Collapsible } from '@cerberus/react/react'
export function BasicDemo() {
return (
Toggle
Collapsible content goes here.
)
}
```
## Initial Open
Use the `defaultOpen` prop to render the collapsible in an open state by default.
```tsx
// components/collapsible/open.demo.tsx
'use client'
import { Box, HStack } from '@/styled-system/jsx'
import { ChevronDown } from '@carbon/icons-react'
import { Collapsible } from '@cerberus/react/react'
export function OpenDemo() {
return (
Toggle
Collapsible content goes here.
)
}
```
## Partial Height
Use the `collapsedHeight` prop to show a preview of the content when collapsed.
```tsx
// components/collapsible/partial.demo.tsx
'use client'
import { Box, HStack } from '@/styled-system/jsx'
import { Button, Collapsible } from '@cerberus/react/react'
export function PartialDemo() {
return (
It is a long established fact that a reader will be distracted by the
readable content of a page when looking at its layout. The point of using
Lorem Ipsum is that it has a more-or-less normal distribution of letters, as
opposed to using 'Content here, content here', making it look like
readable English. Many desktop publishing packages and web page editors now
use Lorem Ipsum as their default model text, and a search for 'lorem
ipsum' will uncover many web sites still in their infancy. Various
versions have evolved over the years, sometimes by accident, sometimes on
purpose (injected humour and the like).
)
}
```
## Effects
To turn off the animation effect, you can set the `effect` prop to `none` on the `Collapsible.Content` component.
```tsx
// components/collapsible/fade.demo.tsx
'use client'
import { Box, HStack } from '@/styled-system/jsx'
import { Collapsible } from '@cerberus/react/react'
export function FadeDemo() {
return (
Toggle
Collapsible content goes here.
)
}
```
## Controlled
Use the `open` and `onOpenChange` props to control the collapsible state.
```tsx
// components/collapsible/controlled.demo.tsx
'use client'
import { Box, Stack } from '@/styled-system/jsx'
import { Collapsible, type OpenChangeDetails, Text } from '@cerberus/react/react'
import { useSignal } from '@cerberus/react/signals'
export function ControlledDemo() {
const [open, setOpen] = useSignal(false)
return (
Status: {open ? 'Open' : 'Closed'} setOpen(e.open)}
w="full"
>
Toggle Collapsible
This collapsible is controlled by external state. You can open and close it
using the buttons above or by clicking the trigger.
)
}
```
## Customization
You can customize the Collapsible component any way you like with style props and data-selectors.
```tsx
// components/collapsible/custom.demo.tsx
'use client'
import { Box } from '@/styled-system/jsx'
import { Collapsible } from '@cerberus/react/react'
export function CustomDemo() {
return (
Custom Toggle
Custom collapsible content.
)
}
```
## Primitives
You can utilize the primitive components to build a custom collapsible solution.
| Component | Description |
| ---------------------- | ---------------------------------------- |
| `CollapsibleRoot` | The context provider of the collapsible. |
| `CollapsibleTrigger` | The button that toggles the collapsible. |
| `CollapsibleIndicator` | The indicator that shows the state. |
| `CollapsibleContent` | The content of the collapsible. |
### Data Attributes
The primitives additionally use the following data attributes for custom styling:
| Name | Value | Description |
| ------------ | -------------------- | ---------------------------- |
| `data-scope` | `collapsible` | The scope of the components. |
| `data-part` | `root` | The root container part. |
| `data-part` | `trigger` | The trigger part. |
| `data-part` | `indicator` | The indicator part. |
| `data-part` | `content` | The content part. |
| `data-state` | `"open" \| "closed"` | The state of the content. |
## API
### Collapsible
The `Collapsible` component is an Object containing all the primitive components.
#### Root Props
| Prop | Type | Required | Description |
| ----------------- | ------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `collapsedHeight` | `string \| number` | No | The height of the content when collapsed. |
| `collapsedWidth` | `string \| number` | No | The width of the content when collapsed. |
| `defaultOpen` | `boolean` | No | The initial open state of the collapsible when rendered. Use when you don't need to control the open state of the collapsible. |
| `disabled` | `boolean` | No | Whether the collapsible is disabled. |
| `effect` | `"none" \| "fadeIin" \| "expandIn"` | No | The animation effect of the content when collapsed. |
| `ids` | `Partial<{ root: string; content: string; trigger: string }>` | No | The ids of the elements in the collapsible. Useful for composition. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | The callback invoked when the exit animation completes. |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | The callback invoked when the open state changes. |
| `open` | `boolean` | No | The controlled open state of the collapsible. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
You can see additional options for the [Root component](https://ark-ui.com/react/docs/components/collapsible#api-reference).
### Parts
The `CollapsibleParts` API is the same as the `Collapsible` component.
---
# Combobox
Category: Components
> A filterable select component that allows users to search and select options.
## Import
```ts
import { Combobox, ComboItemWithIndicator, ComboItemText } from '@cerberus/react'
```
## Usage
The `Combobox` is an abracted API that combines multiple primitives into a single root component. You combine this with the `useStatefulCollection` hook to create a filterable select component.
```tsx
// components/combobox/basic.demo.tsx
'use client'
import { Box, VStack } from '@/styled-system/jsx'
import {
Combobox,
ComboItemText,
ComboItemWithIndicator,
For,
Text,
useStatefulCollection,
} from '@cerberus/react/react'
import { items } from './items'
export function BasicDemo() {
const { collection, handleInputChange } = useStatefulCollection(items)
return (
No results found
}
>
{(item) => (
{item.label}
)}
)
}
```
## With Start Icon
To add an icon to the start position of the input, use the `startIcon` prop to pass in your icon of choice.
```tsx
// components/combobox/start.demo.tsx
'use client'
import { Box, VStack } from '@/styled-system/jsx'
import { Search } from '@carbon/icons-react'
import {
Combobox,
ComboItemText,
ComboItemWithIndicator,
For,
Text,
useStatefulCollection,
} from '@cerberus/react/react'
import { items } from './items'
export function StartIconDemo() {
const { collection, handleInputChange } = useStatefulCollection(items)
return (
}
>
No results found
}
>
{(item) => (
{item.label}
)}
)
}
```
## Sizes
The `Combobox` component supports sizes: `sm` to `lg`. The default is `md`.
```tsx
// components/combobox/size.demo.tsx
'use client'
import { Stack, VStack } from '@/styled-system/jsx'
import {
Combobox,
ComboboxProps,
ComboItemText,
ComboItemWithIndicator,
For,
Text,
useStatefulCollection,
} from '@cerberus/react/react'
import { items } from './items'
import { sizes } from './meta'
export function SizeDemo() {
return (
{(size) => }
)
}
interface SizeBoxProps {
size: ComboboxProps['size']
}
export function SizeBox(props: SizeBoxProps) {
const { collection, handleInputChange } = useStatefulCollection(items)
return (
No results found
}
>
{(item) => (
{item.label}
)}
)
}
```
## With Field
Wrap the `Combobox` component with the `Field` component to add additional functionality such as error and helper text when using it in a form.
```tsx
// components/combobox/field.demo.tsx
'use client'
import { Box, VStack } from '@/styled-system/jsx'
import {
Combobox,
ComboItemText,
ComboItemWithIndicator,
Field,
For,
Text,
useStatefulCollection,
} from '@cerberus/react/react'
import { items } from './items'
export function FieldDemo() {
const { collection, handleInputChange } = useStatefulCollection(items)
return (
No results found
}
>
{(item) => (
{item.label}
)}
)
}
```
## Context
Access the combobox's state with `ComboboxContext` or the `useComboboxContext` hook—useful for displaying the selected value or building custom UI.
```tsx
// components/combobox/context.demo.tsx
'use client'
import { Stack, VStack } from '@/styled-system/jsx'
import { ChevronDown, Close } from '@carbon/icons-react'
import {
ComboboxParts,
ComboItemText,
ComboItemWithIndicator,
For,
Text,
UseComboboxContext,
useStatefulCollection,
} from '@cerberus/react/react'
import { items } from './items'
export function ContextDemo() {
const { collection, handleInputChange } = useStatefulCollection(items)
return (
{(context: UseComboboxContext<(typeof collection.items)[number]>) => (
Selected: {context.valueAsString || 'None'}
)}
Select Relative
No results found
}
>
{(item) => (
{item.label}
)}
)
}
```
## Grouped Items
To group items, use the `ComboItemGroup` component (not ComboboxItemGroup).
```tsx
// components/combobox/group.demo.tsx
'use client'
import { Box, VStack } from '@/styled-system/jsx'
import { Search } from '@carbon/icons-react'
import {
Combobox,
ComboItemGroup,
ComboItemText,
ComboItemWithIndicator,
For,
Text,
useStatefulCollection,
} from '@cerberus/react/react'
import { items } from './items'
export function GroupedItemsDemo() {
const { collection, handleInputChange } = useStatefulCollection(items)
return (
}
>
No results found
}
>
{(item) => (
{item.label}
)}
)
}
```
## Async Loading
Combine the collection with a [query](/docs/signals/create-query) to load data asynchronously with signal-based speed.
```tsx
// components/combobox/loading.demo.tsx
'use client'
import { Box, Square, VStack } from '@/styled-system/jsx'
import { Search } from '@carbon/icons-react'
import {
ComboItemText,
ComboItemWithIndicator,
Combobox,
ComboboxInputValueChangeDetails,
For,
Spinner,
Text,
createListCollection,
} from '@cerberus/react/react'
import { createQuery, useQuery } from '@cerberus/react/signals'
import { Suspense, useState, useTransition } from 'react'
function useDeferredValue() {
// Use native React state and transitions for loading state to override Suspsense
const [inputValue, setInputValue] = useState('')
const [pending, startTransition] = useTransition()
return {
inputValue,
setInputValue,
pending,
startTransition,
}
}
function AsyncCombobox() {
const { inputValue, setInputValue, pending, startTransition } = useDeferredValue()
const data = useQuery(queryList(inputValue))
const collection = createListCollection({
items: data,
itemToString: (item) => item.name,
itemToValue: (item) => item.url,
})
const handleInputChange = (details: ComboboxInputValueChangeDetails) => {
if (details.reason === 'input-change') {
startTransition(() => {
setInputValue(details.inputValue)
})
}
}
return (
) : (
)
}
>
No results found
}
>
{(item) => (
{item.name}
)}
)
}
export function LoadingDemo() {
return (
)
}
// Factories
interface Character {
name: string
height: string
mass: string
created: string
edited: string
url: string
}
const queryList = createQuery(async (inputValue: string) => {
try {
const response = await db.searchCharacters(inputValue)
return response.results
} catch (error) {
console.error(error)
return []
}
}, 'queryList')
// API
const db = {
searchCharacters: async (inputValue: string) => {
const response = await fetch(
`https://swapi.py4e.com/api/people/?search=${inputValue ?? ''}`,
)
return await response.json()
},
}
```
## Highlight Matching Text
Combine the `ComboItemText` component with the `Highlight` component to highlight matching text in the dropdown.
```tsx
// components/combobox/highlight.demo.tsx
'use client'
import { Box, VStack } from '@/styled-system/jsx'
import {
Combobox,
ComboboxContext,
ComboItemText,
ComboItemWithIndicator,
For,
Highlight,
Text,
useStatefulCollection,
} from '@cerberus/react/react'
import { items } from './items'
export function HighlightDemo() {
const { collection, handleInputChange } = useStatefulCollection(items)
return (
No results found
}
>
{(item) => (
{(context) => (
)}
)}
)
}
```
## Creatable
Allow users to create new options when their search doesn't match any existing items. This is useful for tags, categories, or other custom values.
```tsx
// components/combobox/creatable.demo.tsx
'use client'
import { Box, VStack } from '@/styled-system/jsx'
import { Corn } from '@carbon/icons-react'
import {
Combobox,
ComboboxInputValueChangeDetails,
ComboboxOpenChangeDetails,
ComboboxValueChangeDetails,
ComboItemText,
ComboItemWithIndicator,
For,
Show,
Tag,
Text,
useFilter,
useListCollection,
} from '@cerberus/react/react'
import { useSignal } from '@cerberus/react/signals'
import { flushSync } from 'react-dom'
interface Item {
label: string
value: string
__new__?: boolean
}
const NEW_OPTION_VALUE = '[[new]]'
const api = {
create: (value: string): Item => ({ label: value, value: NEW_OPTION_VALUE }),
isNew: (value: string) => value === NEW_OPTION_VALUE,
replaceValue: (values: string[], value: string) => {
return values.map((v) => (v === NEW_OPTION_VALUE ? value : v))
},
getNewOptionData: (inputValue: string): Item => ({
label: inputValue,
value: inputValue,
__new__: true,
}),
}
export function CreatableDemo() {
const [selectedValue, setSelectedValue] = useSignal([])
const [inputValue, setInputValue] = useSignal('')
const { contains } = useFilter({ sensitivity: 'base' })
const { collection, filter, upsert, update, remove } = useListCollection({
initialItems: [
{ label: 'Bug', value: 'bug' },
{ label: 'Feature', value: 'feature' },
{ label: 'Enhancement', value: 'enhancement' },
{ label: 'Documentation', value: 'docs' },
],
filter: contains,
})
const isValidNewOption = (inputValue: string) => {
const exactOptionMatch =
collection.filter((item) => item.toLowerCase() === inputValue.toLowerCase())
.size > 0
return !exactOptionMatch && inputValue.trim().length > 0
}
const handleInputChange = ({
inputValue,
reason,
}: ComboboxInputValueChangeDetails) => {
if (reason === 'input-change' || reason === 'item-select') {
flushSync(() => {
if (isValidNewOption(inputValue)) {
upsert(NEW_OPTION_VALUE, api.create(inputValue))
} else if (inputValue.trim().length === 0) {
remove(NEW_OPTION_VALUE)
}
})
filter(inputValue)
}
setInputValue(inputValue)
}
const handleOpenChange = ({ reason }: ComboboxOpenChangeDetails) => {
if (reason === 'trigger-click') {
filter('')
}
}
const handleValueChange = ({ value }: ComboboxValueChangeDetails) => {
setSelectedValue(api.replaceValue(value, inputValue))
if (value.includes(NEW_OPTION_VALUE)) {
console.log('New Option Created', inputValue)
update(NEW_OPTION_VALUE, api.getNewOptionData(inputValue))
}
}
return (
No results found
}
>
{(item) => (
{item.label}}
>
{() => (
+ Create "{item.label}"
)}
{() => (
New
)}
)}
)
}
```
## Customizing
```tsx
// components/combobox/custom.demo.tsx
'use client'
import { Box, VStack } from '@/styled-system/jsx'
import { ChevronDownOutline } from '@carbon/icons-react'
import {
ComboboxParts,
For,
Portal,
Text,
useStatefulCollection,
} from '@cerberus/react/react'
import { items } from './items'
export function CustomDemo() {
const { collection, handleInputChange } = useStatefulCollection(items)
return (
Custom label
No results found
}
>
{(item) => (
{item.label}
🔥
)}
)
}
```
## Guides
### Router Links
Customize the `navigate` prop on `Combobox` to integrate with your router. Using Tanstack Router:
```tsx
import { Combobox } from '@cerberus/react'
import { useNavigate } from '@tanstack/react-router'
function Demo() {
const navigate = useNavigate()
return (
{
navigate({ to: e.node.href })
}}
>
{/* ... */}
)
}
```
### Custom Objects
By default, the combobox collection expects an array of objects with label and value properties. In some cases, you may need to deal with custom objects.
Use the `itemToString` and `itemToValue` props to map the custom object to the required interface.
```ts
const items = [
{ country: 'United States', code: 'US', flag: '🇺🇸' },
{ country: 'Canada', code: 'CA', flag: '🇨🇦' },
{ country: 'Australia', code: 'AU', flag: '🇦🇺' },
// ...
]
const { collection } = useListCollection({
initialItems: items,
itemToString: (item) => item.country,
itemToValue: (item) => item.code,
})
```
### Type Safety
The `ComboboxRootProps` type enables you to create typed wrapper components that maintain full type safety for collection items.
```tsx
const Combobox = (props: ComboboxRootProps) => {
return {/* ... */}
}
```
### Large Datasets
The recommended way of managing large lists is to use the `limit` property on the `useListCollection` hook. This will limit the number of rendered items in the DOM to improve performance.
```ts
const { collection } = useListCollection({
initialItems: items,
limit: 10,
})
```
### Available Size
The following css variables are exposed to the `ComboboxPositioner` which you can use to style the `ComboboxContent`.
```css
/* width of the combobox control */
--reference-width: ;
/* width of the available viewport */
--available-width: ;
/* height of the available viewport */
--available-height: ;
```
For example, if you want to make sure the maximum height doesn't exceed the available height, you can use the following:
```css
[data-scope='combobox'][data-part='content'] {
max-height: calc(var(--available-height) - 100px);
}
```
## Primitives
You can utilize the primitive components or the `css` prop to customize the Combobox.
| Component | Description |
| ------------------------ | ----------------------------------------------------------- |
| `ComboboxRoot` | The context provider for the combobox family |
| `ComboboxLabel` | The label that appears above the combobox input |
| `ComboboxControl` | The wrapper to the combobox trigger that opens the dropdown |
| `ComboboxInput` | The input field of the combobox |
| `ComboboxTrigger` | he trigger that opens the dropdown |
| `ComboboxClearTrigger` | The trigger that clears the selected value |
| `ComboboxPositioner` | The wrapper that positions the dropdown |
| `ComboboxContent` | The content of the dropdown (i.e. the container itself) |
| `ComboboxItemGroup` | The group of options in the dropdown |
| `ComboboxItemGroupLabel` | The label for the group of options |
| `ComboboxItem` | The option in the dropdown |
| `ComboboxItemText` | The text label of the option |
| `ComboboxItemIndicator` | The indicator shown when the option is selected |
## API
### Combobox
The `Combobox` component is an abstraction of our primitives and accepts the following props:
| Name | Default | Description |
| ----------- | ------- | ---------------------------------------------- |
| `size` | `md` | The size of the combobox. |
| `startIcon` | | The icon to display at the start of the input. |
The `Combobox` component also accepts all the props of the [`ComboboxRoot` primitive props](https://ark-ui.com/react/docs/components/combobox#api-reference)
### ItemWithIndicator
The `ItemWithIndicator` component is an abstraction of our primitives and accepts all the props of the [`ComboboxItem` primitive props](https://ark-ui.com/react/docs/components/combobox#api-reference)
### ComboItemGroup
The `ComboItemGroup` component is an abstraction of our primitives and accepts the following props:
| Name | Default | Description |
| ------- | ------- | ----------------------- |
| `label` | | The label of the group. |
The `ComboItemGroup` component is an abstraction of our primitives and accepts all the props of the [`ComboboxItemGroup` primitive props](https://ark-ui.com/react/docs/components/combobox#api-reference)
## Primitive API
### Root Props
| Prop | Type | Required | Description |
| ------------------------- | ---------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `collection` | `ListCollection` | Yes | The collection of items |
| `allowCustomValue` | `boolean` | No | Whether to allow typing custom values in the input |
| `alwaysSubmitOnEnter` | `boolean` | No | Whether to always submit on Enter key press, even if popup is open. |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `autoFocus` | `boolean` | No | Whether to autofocus the input on mount |
| `closeOnSelect` | `boolean` | No | Whether to close the combobox when an item is selected. |
| `autoFocus` | `boolean` | No | Whether to autofocus the input on mount |
| `closeOnSelect` | `boolean` | No | Whether to close the combobox when an item is selected. |
| `composite` | `boolean` | No | Whether the combobox is a composed with other composite widgets like tabs |
| `defaultHighlightedValue` | `string` | No | The initial highlighted value of the combobox when rendered. Use when you don't need to control the highlighted value of the combobox. |
| `defaultInputValue` | `string` | No | The initial value of the combobox's input when rendered. Use when you don't need to control the value of the combobox's input. |
| `defaultOpen` | `boolean` | No | The initial open state of the combobox when rendered. Use when you don't need to control the open state of the combobox. |
| `defaultValue` | `string[]` | No | The initial value of the combobox's selected items when rendered. Use when you don't need to control the value of the combobox's selected items. |
| `disabled` | `boolean` | No | Whether the combobox is disabled |
| `disableLayer` | `boolean` | No | Whether to disable registering this a dismissable layer |
| `form` | `string` | No | The associate form of the combobox. |
| `highlightedValue` | `string` | No | The controlled highlighted value of the combobox |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial` | No | The ids of the elements in the combobox. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `inputBehavior` | `'none' \| 'autohighlight' \| 'autocomplete'` | No | Defines the auto-completion behavior of the combobox. `autohighlight`: The first focused item is highlighted as the user types, `autocomplete`: Navigating the listbox with the arrow keys selects the item and the input is updated |
| `inputValue` | `string` | No | The controlled value of the combobox's input |
| `invalid` | `boolean` | No | Whether the combobox is invalid |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `loopFocus` | `boolean` | No | Whether to loop the keyboard navigation through the items |
| `multiple` | `boolean` | No | Whether to allow multiple selection. **Good to know:** When `multiple` is `true`, the `selectionBehavior` is automatically set to `clear`. It is recommended to render the selected items in a separate container. |
| `name` | `string` | No | The `name` attribute of the combobox's input. Useful for form submission |
| `navigate` | `(details: NavigateDetails) => void` | No | Function to navigate to the selected item |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onHighlightChange` | `(details: HighlightChangeDetails) => void` | No | Function called when an item is highlighted using the pointer or keyboard navigation. |
| `onInputValueChange` | `(details: InputValueChangeDetails) => void` | No | Function called when the input's value changes |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Function called when the popup is opened |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onSelect` | `(details: SelectionDetails) => void` | No | Function called when an item is selected |
| `onValueChange` | `(details: ValueChangeDetails) => void` | No | Function called when a new item is selected |
| `open` | `boolean` | No | The controlled open state of the combobox |
| `openOnChange` | `boolean` | No | Whether to show the combobox when the input value changes |
| `openOnClick` | `boolean` | No | Whether to open the combobox popup on initial click on the input |
| `openOnKeyPress` | `boolean` | No | Whether to open the combobox on arrow key press |
| `placeholder` | `string` | No | The placeholder text of the combobox's input |
| `positioning` | `PositioningOptions` | No | The positioning options to dynamically position the menu |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `readOnly` | `boolean` | No | Whether the combobox is readonly. This puts the combobox in a "non-editable" mode but the user can still interact with it |
| `required` | `boolean` | No | Whether the combobox is required |
| `scrollToIndexFn` | `(details: ScrollToIndexDetails) => void` | No | Function to scroll to a specific index |
| `selectionBehavior` | `'clear' \| 'replace' \| 'preserve'` | No | The behavior of the combobox input when an item is selected. `replace`: The selected item string is set as the input value, `clear`: The input value is cleared, `preserve`: The input value is preserved |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `translations` | `IntlTranslations` | No | Specifies the localized strings that identifies the accessibility elements and their states |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
| `value` \| `string[]` | No | No | The controlled value of the combobox's selected items |
### Root Data Attributes:
| Attribute | Value |
| ----------------- | ---------------------- |
| `[data-scope]` | combobox |
| `[data-part]` | root |
| `[data-invalid]` | Present when invalid |
| `[data-readonly]` | Present when read-only |
### ClearTrigger Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### ClearTrigger Data Attributes:
| Attribute | Value |
| ---------------- | -------------------- |
| `[data-scope]` | combobox |
| `[data-part]` | clear-trigger |
| `[data-invalid]` | Present when invalid |
### Content Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Content Data Attributes:
| Attribute | Value |
| ------------------- | --------------------------------- |
| `[data-scope]` | combobox |
| `[data-part]` | content |
| `[data-state]` | "open" \| "closed" |
| `[data-nested]` | listbox |
| `[data-has-nested]` | listbox |
| `[data-placement]` | The placement of the content |
| `[data-empty]` | Present when the content is empty |
#### Content CSS Variables:
| Variable | Description |
| ---------------------- | ----------------------------------------------- |
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested comboboxs |
### Control Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Control Data Attributes:
| Attribute | Value |
| ----------------- | --------------------- |
| `[data-scope]` | combobox |
| `[data-part]` | control |
| `[data-state]` | "open" \| "closed" |
| `[data-focus]` | Present when focused |
| `[data-disabled]` | Present when disabled |
| `[data-invalid]` | Present when invalid |
### Input Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Input Data Attributes:
| Attribute | Value |
| ------------------ | -------------------- |
| `[data-scope]` | combobox |
| `[data-part]` | input |
| `[data-invalid]` | Present when invalid |
| `[data-autofocus]` | |
| `[data-state]` | "open" \| "closed" |
#### ItemGroupLabel Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
### ItemGroup Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### ItemGroup Data Attributes:
| Attribute | Value |
| -------------- | --------------------------------- |
| `[data-scope]` | combobox |
| `[data-part]` | item-group |
| `[data-empty]` | Present when the content is empty |
### ItemIndicator Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### ItemIndicator Data Attributes:
| Attribute | Value |
| -------------- | ------------------------ |
| `[data-scope]` | combobox |
| `[data-part]` | item-indicator |
| `[data-state]` | "checked" \| "unchecked" |
### Item Props:
| Prop | Type | Required | Description |
| -------------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `item` | `any` | No | The item to render |
| `persistFocus` | `boolean` | No | Whether hovering outside should clear the highlighted state |
#### Item Data Attributes:
| Attribute | Value |
| -------------------- | ------------------------ |
| `[data-scope]` | combobox |
| `[data-part]` | item |
| `[data-highlighted]` | Present when highlighted |
| `[data-state]` | "checked" \| "unchecked" |
| `[data-disabled]` | Present when disabled |
| `[data-value]` | The value of the item |
### ItemText Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### ItemText Data Attributes:
| Attribute | Value |
| -------------------- | ------------------------ |
| `[data-scope]` | combobox |
| `[data-part]` | item-text |
| `[data-state]` | "checked" \| "unchecked" |
| `[data-disabled]` | Present when disabled |
| `[data-highlighted]` | Present when highlighted |
### Label Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Label Data Attributes:
| Attribute | Value |
| ----------------- | ---------------------- |
| `[data-scope]` | combobox |
| `[data-part]` | label |
| `[data-readonly]` | Present when read-only |
| `[data-disabled]` | Present when disabled |
| `[data-invalid]` | Present when invalid |
| `[data-required]` | Present when required |
| `[data-focus]` | Present when focused |
### List Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### List Data Attributes:
| Attribute | Value |
| -------------- | --------------------------------- |
| `[data-scope]` | combobox |
| `[data-part]` | list |
| `[data-empty]` | Present when the content is empty |
### Positioner Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Positioner CSS Variables:
| Variable | Description |
| -------------------- | ----------------------------------- |
| `--reference-width` | The width of the reference element |
| `--reference-height` | The height of the root |
| `--available-width` | The available width in viewport |
| `--available-height` | The available height in viewport |
| `--x` | The x position for transform |
| `--y` | The y position for transform |
| `--z-index` | The z-index value |
| `--transform-origin` | The transform origin for animations |
### RootProvider Props:
| Prop | Type | Required | Description |
| ---------------------- | ---------------------- | -------- | --------------------------------------------------------------------------------------------------- |
| `value` | `UseComboboxReturn` | Yes | |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
### Trigger Props:
| Prop | Type | Required | Description |
| ----------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `focusable` | `boolean` | No | Whether the trigger is focusable |
#### Trigger Data Attributes:
| Attribute | Value |
| ------------------ | ---------------------- |
| `[data-scope]` | combobox |
| `[data-part]` | trigger |
| `[data-state]` | "open" \| "closed" |
| `[data-invalid]` | Present when invalid |
| `[data-focusable]` | |
| `[data-readonly]` | Present when read-only |
| `[data-disabled]` | Present when disabled |
## Hooks
### useStatefulCollection
The `useStatefulCollection` function is a utility hook that creates a collection of options and filters the list based on the user input.
#### Returns
| Name | Description |
| ----------------- | ---------------------------------------------- |
| collection | The collection of options. |
| filterChars | The filter value split into an Array of chars. |
| handleInputChange | The function to pass to `onInputValueChange`. |
### Parts
The `ComboboxParts` API is an Object containing the full family of components.
> **Note:** It is best to only use the ComboboxParts if you are building a custom solution. Importing Object based components will ship every property it includes into your bundle, regardless if you use it or not.
| Name | Description |
| ---------------- | -------------------------------------------------------------------------------------- |
| `Root` | The `ComboboxRoot` component which is the Provider for the family. |
| `Label` | The `ComboboxLabel` component which displays the label. |
| `Control` | The `ComboboxControl` component which is the container for the visual field. |
| `Input` | The `ComboboxInput` component which is the visual field. |
| `Trigger` | The `ComboboxTrigger` component which is the trigger for the dropdown. |
| `ClearTrigger` | The `ComboboxClearTrigger` component which is the trigger to clear the value. |
| `Positioner` | The `ComboboxPositioner` component which is controls the positioning for the dropdown. |
| `Content` | The `ComboboxContent` component which is the dropdown itself. |
| `ItemGroup` | The `ComboboxItemGroup` component which is the group of options in the dropdown. |
| `ItemGroupLabel` | The `ComboboxItemGroupLabel` component which is the label for the group of options. |
| `Item` | The `ComboboxItem` component which is the option in the dropdown. |
| `ItemText` | The `ComboboxItemText` component which is the text label of the option. |
| `ItemIndicator` | The `ComboboxItemIndicator` component which displays based on the `checked` state. |
---
# Composition
Category: Concepts
> Learn how to compose components in Cerberus.
## 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.
```tsx
```
In this example, the `asChild` prop allows the link to be used as the
button (e.g., a button-styled link).
## 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.
### Advanced Usage Example
```tsx
const MyComponent = forwardRef((props, ref) => {
return
})
```
## The Text Component
The `Text` component allows you to change the underlying HTML element that a React component renders via the `as` prop. It provides a straightforward way to change the underlying element while retaining the component's functionality.
This is to help reduce the amount of JSX you need to write for typography driven UI.
```tsx
Hello, world!
```
This is only available for the Text component which is a wrapper
around our text-related patterns.
>
}
/>
---
# Confirm Modal
Category: Components
> The Confirm Modal component provides a way to confirm actions with users, ensuring they are aware of the consequences before proceeding.
## Import
```ts
import { ConfirmModal, useConfirmModal } from '@cerberus/react'
```
## Usage
The `ConfirmModal` component is a controlled abstraction of the [Dialog](/docs/components/dialog).
```tsx
// components/confirm-modal/basic.demo.tsx
'use client'
import { HStack } from '@/styled-system/jsx'
import {
Button,
ConfirmModal,
Show,
Text,
useConfirmModal,
} from '@cerberus/react/react'
import { useSignal } from '@cerberus/react/signals'
import Link from 'next/link'
export function BasicDemo() {
return (
)
}
function NonDestructiveFeature() {
const confirm = useConfirmModal()
const [consent, setConsent] = useSignal(null)
const handleClick = async () => {
const userConsent = await confirm.show({
heading: 'Add new payment method?',
description: (
<>
This will add a new payment method to your account to be billed for future
purchases. Learn more.
>
),
actionText: 'Yes, add payment method',
cancelText: 'No, cancel',
})
setConsent(userConsent)
}
return (
User consent:{' '}
{JSON.stringify(consent)}
)
}
```
## Destructive
To show a destructive confirm modal, set the `kind` prop in the options to `'destructive'`.
```tsx
// components/confirm-modal/destructive.demo.tsx
'use client'
import { HStack } from '@/styled-system/jsx'
import {
Button,
ConfirmModal,
Show,
Text,
useConfirmModal,
} from '@cerberus/react/react'
import { useSignal } from '@cerberus/react/signals'
export function DestructiveDemo() {
return (
)
}
function DestructiveFeature() {
const confirm = useConfirmModal()
const [consent, setConsent] = useSignal(null)
const handleClick = async () => {
const userConsent = await confirm.show({
kind: 'destructive',
heading: 'Remove payment method?',
description: 'This is a permanent action and cannot be undone.',
actionText: 'Yes, delete',
cancelText: 'Cancel',
})
setConsent(userConsent)
}
return (
User consent:{' '}
{JSON.stringify(consent)}
)
}
```
### No Avatar
To change the visibility of the avatar, set `showAvatar` to `false` in the options.
```tsx
// components/confirm-modal/avatar.demo.tsx
'use client'
import { HStack } from '@/styled-system/jsx'
import {
Button,
ConfirmModal,
Show,
Text,
useConfirmModal,
} from '@cerberus/react/react'
import { useSignal } from '@cerberus/react/signals'
import Link from 'next/link'
export function NoAvatarDemo() {
return (
)
}
function Feature() {
const confirm = useConfirmModal()
const [consent, setConsent] = useSignal(null)
const handleClick = async () => {
const userConsent = await confirm.show({
showAvatar: false,
heading: 'Add new payment method?',
description: (
<>
This will add a new payment method to your account to be billed for future
purchases. Learn more.
>
),
actionText: 'Yes, add payment method',
cancelText: 'No, cancel',
})
setConsent(userConsent)
}
return (
User consent:{' '}
{JSON.stringify(consent)}
)
}
```
## Primitives
The layers of the `ConfirmModal` which can be used to create a fully custom solution are the same as the [Dialog](/docs/components/dialog#primitives).
### Data Attributes
The primitives additionally use the same data attributes as the [Dialog](/docs/components/dialog#primitives).
## API
### ConfirmModal
The `ConfirmModal` component is an abstraction of the Dialog and accepts the same
props as the [`Dialog`](/docs/components/dialog#api) component.
### Show Method Options
The `show` method accepts the following options:
| Name | Default | Description |
| ------------- | ----------------- | ---------------------------------------------------------------------------------- |
| `showAvatar` | `true` | Whether to show the avatar in the modal. |
| `kind` | `non-destructive` | The variant used to theme the modal. |
| `heading` | | The heading of the modal. |
| `description` | | The description of the modal. For non-destructive modals only, can be a ReactNode. |
| `actionText` | | The text for the action button. |
| `cancelText` | | The text for the cancel button. |
---
# Container Query
Category: Layout
> Apply styles to an element based on certain attributes of its container.
## Import
```ts
import { Cq } from 'styled-system/jsx'
```
## Usage
The `Cq` component is used to apply styles based on the width of the **container**. This is a useful alternative to media queries which are based on the width of the browser viewport.
`Cq` provides two utility props:
- `name`: The name of the container query, Maps to the `containerName` [CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/container-name).
- `type`: The type of the container query. Maps to the `containerType` [CSS property](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/container-type). Defaults to `inline-size`.
The container query size conditions use the following syntax: `@/[size]` or `@[custom-name]/[size]`
```tsx
// components/cq/basic.demo.tsx
import { DecorativeBox } from '@/components/decorative-box'
import { Cq, VStack } from '@/styled-system/jsx'
export function BasicDemo() {
return (
)
}
function ContainerBox() {
return (
Responds to the container size change - resize the browser to see
)
}
```
### Name
The `name` prop is used to define the name of the container query. This must be defined in the Panda config in order to use it on the `Cq` component.
Once "registered", the `name` prop can be used to add container-specific boundaries and styles.
#### Step 1: Register container names and sizes
```ts title="panda.config.ts"
export default createCerberusConfig({
// ...
theme: {
extend: {
containerNames: ['sidebar', 'content'],
containerSizes: {
xs: '40em',
sm: '60em',
md: '80em',
},
},
},
})
```
#### Step 2: Use `Cq` to apply container query styles
```tsx
import { Box, Cq } from 'styled-system/jsx'
export function UsageDemo() {
return (
)
}
```
### Type
The `type` prop is used to define the type of the container query. Defaults to `inline-size`.
```tsx
// components/cq/type.demo.tsx
import { DecorativeBox } from '@/components/decorative-box'
import { Cq, VStack } from '@/styled-system/jsx'
export function TypeDemo() {
return (
)
}
function ContainerBox() {
return (
Responds to the container size change - resize the browser to see
)
}
```
## Guide
### Default Sizes
The following sizes are included with Cerberus by default:
```ts
{
xs: '320px',
sm: '384px',
md: '448px',
lg: '512px',
xl: '576px',
'2xl': '672px',
'3xl': '768px',
'4xl': '896px',
'5xl': '1024px',
'6xl': '1152px',
'7xl': '1280px',
'8xl': '1440px'
}
```
## Props
The `Cq` component supports all CSS properties as props, making it easy to
style elements.
---
# Container
Category: Layout
> Used to constrain a content's width to the current breakpoint, while keeping it fluid.
## Import
```ts
import { Container } from 'styled-system/jsx'
```
## Usage
The default `maxWidth` is `8xl` which maps to `90rem (1440px)`.
```tsx
// components/container/basic.demo.tsx
import { DecorativeBox } from '@/components/decorative-box'
import { Container } from 'styled-system/jsx'
export function BasicDemo() {
return (
Content in a Container
)
}
```
### Sizes
Use the `maxWidth` prop to change the size of the container.
```tsx
// components/container/sizes.demo.tsx
import { Container, VStack } from 'styled-system/jsx'
import { DecorativeBox } from '@/components/decorative-box'
function getText(size: string) {
return `This is some text within a container of size ${size}.`
}
export function SizesDemo() {
return (
{getText('1/4')}{getText('1/2')}{getText('3/4')}{getText('full')}
)
}
```
## Props
The `Container` component accepts any CSS properties.
---
# CTA Modal
Category: Components
> A modal component for displaying call-to-action messages.
## Import
```ts
import { CTAModal, useCTAModal, createCTAModalActions } from '@cerberus/react'
```
## Usage
To use the CTAModal, wrap your component with the `CTAModal` provider and use the `useCTAModal` hook to access the `show` method.
To create your actions, use the `createCTAModalActions` function.
```tsx
// components/cta-modal/basic.demo.tsx
'use client'
import { HStack } from '@/styled-system/jsx'
import {
Button,
createCTAModalActions,
CTAModal,
useCTAModal,
} from '@cerberus/react/react'
export function BasicDemo() {
return (
)
}
function CTAFeature() {
const { show } = useCTAModal()
const handleClick = () => {
show({
heading: 'Copy or create a Cohort',
description: 'Create a new cohort or copy an existing one.',
actions: createCTAModalActions([
{
text: 'Create new',
handleClick: () => alert('Create new'),
},
{
text: 'Copy existing',
handleClick: () => alert('Copy existing'),
},
]),
})
}
return (
)
}
```
## With Links
To use links instead of buttons, pass your link components to the `createCTAModalActions` function.
```tsx
// components/cta-modal/links.demo.tsx
'use client'
import { HStack } from '@/styled-system/jsx'
import { ArrowRight } from '@carbon/icons-react'
import {
Button,
createCTAModalActions,
CTAModal,
useCTAModal,
} from '@cerberus/react/react'
import Link from 'next/link'
export function LinksDemo() {
return (
)
}
function CTALinkFeature() {
const { show } = useCTAModal()
const handleLinkClick = () => {
show({
heading: 'Copy or create a Cohort',
description: 'Create a new cohort or copy an existing one.',
actions: createCTAModalActions([
See Button Docs
,
See Dialog Docs
,
]),
})
}
return (
)
}
```
## With Content
To use a custom content component, pass your custom component to the `content` prop of the `show` method.
```tsx
// components/cta-modal/description.demo.tsx
'use client'
import { HStack, VStack } from '@/styled-system/jsx'
import {
Button,
createCTAModalActions,
CTAModal,
Field,
Input,
Textarea,
useCTAModal,
} from '@cerberus/react/react'
export function DescriptionDemo() {
return (
)
}
function CTADescriptionFeature() {
const { show } = useCTAModal()
const handleClick = () => {
show({
heading: 'Copy or create a Cohort',
content: (
),
actions: createCTAModalActions([
{
text: 'Create new',
handleClick: () => alert('Create new'),
},
{
text: 'Copy existing',
handleClick: () => alert('Copy existing'),
},
]),
})
}
return (
)
}
```
## Primitives
The CTAModal is an abstraction of the [Dialog Primitives](/docs/components/dialog#primitives) and does not provide any additional customization. To build your own CTAModal, use the [`Dialog`](/docs/components/dialog) components.
## API
The `CTAModal` component is an abstraction of the Dialog and accepts the same
props as the [`Dialog`](/docs/components/dialog) component.
### Show Method Options
The `show` method accepts the following options:
| Name | Default | Description |
| ------------- | ----------- | --------------------------------------------------------------------------------------------- |
| `heading` | `void` | The heading of the modal. |
| `description` | `void`` | The description of the modal. |
| `icon` | `undefined` | The icon to display in the modal. |
| `actions` | `[]` | The actions to display in the modal. **Requires the use of `createCTAModalActions` utility.** |
### Utilities
The `createCTAModalActions` utility accepts an array of objects with the following properties or ReactNodes:
## Action
| Name | Default | Description |
| ------------- | ----------- | -------------------------------- |
| `text` | `''` | The text of the action. |
| `handleClick` | `undefined` | The click handler of the action. |
## Link
Pass your Array of link components to the `createCTAModalActions` function.
---
# Date Picker
Category: Components
> A date picker component for selecting single or range dates.
## Import
```ts
import {
DatePicker,
DatePickerLabel,
DatePickerInput,
DatePickerCalendar,
} from '@cerberus/react'
```
## Usage
The DatePicker component is a controlled component that allows users to select a single date or a range of dates. It consists of a label, input field, and a calendar view.
### Single Date Picker
```tsx
// components/date-picker/basic.demo.tsx
import { Box } from '@/styled-system/jsx'
import {
DatePicker,
DatePickerCalendar,
DatePickerInput,
DatePickerLabel,
Field,
} from '@cerberus/react/react'
export function BasicDemo() {
return (
Start date
)
}
```
### Date Range Picker
To create a date range picker, set the `selectionMode` prop to `range` on the `DatePicker` component and use the `RangePickerInput` component as the input field.
```tsx
// components/date-picker/range.demo.tsx
import { Box } from '@/styled-system/jsx'
import {
DatePicker,
DatePickerCalendar,
DatePickerLabel,
Field,
RangePickerInput,
} from '@cerberus/react/react'
export function RangeDemo() {
return (
Search range
)
}
```
#### Using Default Values
You can set the default values for the range date picker by passing the `defaultValue` prop to the `DatePicker` component in the form of an Array of two date strings.
> **Note:** The `defaultValue` prop is only used when the component is first rendered. If you need to update the value dynamically, use the `value` prop instead.
```tsx
// components/date-picker/rangeDefault.demo.tsx
'use client'
import { Box } from '@/styled-system/jsx'
import {
DatePicker,
DatePickerCalendar,
DatePickerLabel,
Field,
parseDate,
RangePickerInput,
} from '@cerberus/react/react'
export function RangeDefaultValueDemo() {
const defaultValue = parseDate(['2025-01-01', '2025-01-15'])
return (
Search range
)
}
```
### Custom Styles
You can customize the DatePicker width and other styles using the `css` prop along with the `data-part` selector to target specific primitives.
```tsx
// components/date-picker/custom.demo.tsx
import { Grid } from '@/styled-system/jsx'
import {
DatePicker,
DatePickerCalendar,
DatePickerInput,
DatePickerLabel,
Field,
} from '@cerberus/react/react'
export function CustomDemo() {
return (
Start dateEnd date
)
}
```
## Date Helpers
The `DatePicker` component relies on different date utilities to handle date parsing and formatting. Here are the options available:
### Creating Dates
You can create a structured date Object using the `CalendarDate` class. This function accepts a date string or an object with year, month, and day properties.
```ts
import { CalendarDate } from '@cerberus/react'
const date = new CalendarDate(2022, 2, 3)
```
View the{' '}
CalendarDate documentation
{' '}
for more details on how to use it.
>
}
/>
### Parsing Dates
You can use the `parseDate` function to convert an array of date strings into a date object that the `DatePicker` can understand.
```ts
import { parseDate } from '@cerberus/react'
const date = parseDate(['2025-01-01', '2025-01-15'])
```
View the{' '}
parseDate documentation
{' '}
for more details on how to use it.
>
}
/>
### Today's Date
You can use the `today` function to get the current date in a format that the `DatePicker` can understand.
```ts
import { today, getLocalTimeZone } from '@cerberus/react'
const nyDate = today('America/New_York')
const localDate = today(getLocalTimeZone())
```
View the{' '}
today documentation
{' '}
for more details on how to use it.
>
}
/>
### Formatting Dates
You can use the `DateFormatter` class to format a date object into a string. This is useful for displaying dates in a human-readable format.
```ts
import { DateFormatter } from '@cerberus/react'
const formatter = new DateFormatter('en-US', {
dateStyle: 'long',
timeZone: 'America/New_York',
})
const date = formatter.format(new Date()) // "January 1, 2025"
```
View the{' '}
DateFormatter documentation
{' '}
for more details on how to use it.
>
}
/>
## Primitives
You can utilize the primitive components or the `css` prop to customize the date picker.
| Component | Description |
| ------------------------ | --------------------------------------------------- |
| `DatePickerRoot` | The main container for the date-picker items. |
| `DatePickerLabel` | The label for the date-picker. |
| `DatePickerControl` | The control for the date-picker. |
| `DatePickerInput` | The input field and trigger for the date-picker. |
| `DatePickerTrigger` | The trigger for the date-picker. |
| `DatePickerClearTrigger` | The trigger to clear the date-picker. |
| `DatePickerPositioner` | The positioner for the date-picker. |
| `DatePickerContent` | The content of the date-picker calendar. |
| `DatePickerYearSelect` | The select input for the year in the date-picker. |
| `DatePickerMonthSelect` | The select input for the month in the date-picker. |
| `DatePickerView` | The container (view) of the calendar. |
| `DatePickerContext` | The context provider for the date-picker calendar. |
| `DatePickerViewControl` | The triggers that update the calendar in the view. |
| `DatePickerPrevTrigger` | The trigger to navigate to the previous view. |
| `DatePickerNextTrigger` | The trigger to navigate to the next view. |
| `DatePickerViewTrigger` | The trigger to navigate to a specific view. |
| `DatePickerRangeText` | The text that displays the range of dates selected. |
| `DatePickerTable` | The table that contains the calendar. |
| `DatePickerTableRow` | The row of the calendar table. |
| `DatePickerTableHead` | The header of the calendar table. |
| `DatePickerTableHeader` | The header cell of the calendar table. |
| `DatePickerTableBody` | The body of the calendar table. |
| `DatePickerTableCell` | The cell of the calendar table. |
| `DatePickerTableTrigger` | The trigger of the cell in the calendar table. |
### Data Attributes
The Primitives additionally use the following data attributes for custom styling:
| Name | Value | Description |
| ------------ | ------------------ | ---------------------------- |
| `data-scope` | `date-picker` | The scope of the components. |
| `data-part` | `root` | The root container part. |
| `data-part` | `label` | The label part. |
| `data-part` | `control` | The control part. |
| `data-part` | `input` | The input part. |
| `data-part` | `trigger` | The trigger part. |
| `data-part` | `clearTrigger` | The clear trigger part. |
| `data-part` | `positioner` | The positioner part. |
| `data-part` | `content` | The content part. |
| `data-part` | `viewControl` | The view control part. |
| `data-part` | `prevTrigger` | The previous trigger part. |
| `data-part` | `nextTrigger` | The next trigger part. |
| `data-part` | `viewTrigger` | The view trigger part. |
| `data-part` | `rangeText` | The range text part. |
| `data-part` | `yearSelect` | The year select part. |
| `data-part` | `monthSelect` | The month select part. |
| `data-part` | `view` | The view part. |
| `data-part` | `table` | The table part. |
| `data-part` | `tableHead` | The table head part. |
| `data-part` | `tableBody` | The table body part. |
| `data-part` | `tableRow` | The table row part. |
| `data-part` | `tableHeader` | The table header part. |
| `data-part` | `tableCell` | The table cell part. |
| `data-part` | `tableCellTrigger` | The table cell trigger part. |
## API
### Props
#### DatePicker
The `DatePicker` component accepts all the props of the [`DatePickerRoot` primitive](https://ark-ui.com/react/docs/components/date-picker#api-reference)
#### DatePickerLabel
The `DatePickerLabel` component accepts all the props of the [`DatePickerLabel` primitive](https://ark-ui.com/react/docs/components/date-picker#api-reference)
#### DatePickerInput
The `DatePickerInput` component accepts all the props of the [`DatePickerInputEl` primitive](https://ark-ui.com/react/docs/components/date-picker#api-reference)
### Parts
The `DatePickerParts` API is an Object containing the full family of components.
> **Note:** It is best to only use the DatePickerParts if you are building a custom solution. Importing Object based components will ship every property it includes into your bundle, regardless if you use it or not.
| Name | Description |
| -------------- | ---------------------------------------------------------------------------------------- |
| `Root` | The `DatePickerRoot` component and main container for the date-picker items. |
| `Label` | The `DatePickerLabel` component and label for the date-picker. |
| `Control` | The `DatePickerControl` component and control for the date-picker. |
| `Input` | The `DatePickerInput` component and input field and trigger for the date-picker. |
| `Trigger` | The `DatePickerRoot` component and trigger for the date-picker. |
| `ClearTrigger` | The `DatePickerClearTrigger` component and trigger to clear the date-picker. |
| `Positioner` | The `DatePickerPositioner` component and positioner for the date-picker. |
| `Content` | The `DatePickerContent` component and content of the date-picker calendar. |
| `YearSelect` | The `DatePickerYearSelect` component and select input for the year in the date-picker. |
| `MonthSelect` | The `DatePickerMonthSelect` component and select input for the month in the date-picker. |
| `View` | The `DatePickerView` component and container (view) of the calendar. |
| `Context` | The `DatePickerContext` component and context provider for the date-picker calendar. |
| `ViewControl` | The `DatePickerViewControl` component and triggers that update the calendar in the view. |
| `PrevTrigger` | The `DatePickerPrevTrigger` component and trigger to navigate to the previous view. |
| `NextTrigger` | The `DatePickerNextTrigger` component and trigger to navigate to the next view. |
| `ViewTrigger` | The `DatePickerViewTrigger` component and trigger to navigate to a specific view. |
| `RangeText` | The `DatePickerRangeText` component and text that displays the range of dates selected. |
| `Table` | The `DatePickerTable` component and table that contains the calendar. |
| `TableRow` | The `DatePickerTableRow` component and row of the calendar table. |
| `TableHead` | The `DatePickerTableHead` component and header of the calendar table. |
| `TableHeader` | The `DatePickerTableHeader` component and header cell of the calendar table. |
| `TableBody` | The `DatePickerTableBody` component and body of the calendar table. |
| `TableCell` | The `DatePickerTableCell` component and cell of the calendar table. |
| `TableTrigger` | The `DatePickerTableTrigger` component and trigger of the cell in the calendar table. |
---
# Dialog
Category: Components
> A modal window that appears on top of the main content.
## Import
```ts
import {
Dialog,
DialogTrigger,
DialogHeading,
DialogDescription,
DialogCloseTrigger,
DialogCloseIconTrigger,
} from '@cerberus-design/react'
```
## Usage
To use the Dialog component, wrap it in a `DialogProvider` and use the `DialogTrigger` component to open the dialog. The `Dialog` component is a controlled component that can be used to display content in a modal.
```tsx
// components/dialog/basic.demo.tsx
'use client'
import { Box, Stack } from '@/styled-system/jsx'
import {
Button,
Dialog,
DialogCloseIconTrigger,
DialogCloseTrigger,
DialogDescription,
DialogHeading,
DialogProvider,
DialogTrigger,
} from '@cerberus/react/react'
export function BasicDemo() {
return (
)
}
```
### Sizes
The Dialog component supports different sizes. You can set the size prop to `xs` to `lg`, or `full`.
```tsx
// components/dialog/sizes.demo.tsx
'use client'
import { Box, HStack, Stack } from '@/styled-system/jsx'
import {
Button,
Dialog,
DialogCloseIconTrigger,
DialogCloseTrigger,
DialogDescription,
DialogHeading,
DialogProps,
DialogProvider,
DialogTrigger,
For,
} from '@cerberus/react/react'
import recipesSpec from 'styled-system/specs/recipes.json'
const variants = (recipesSpec.data.find((r) => r.name === 'dialog')?.variants.size ??
[]) as DialogProps['size'][]
export function SizesDemo() {
return (
{(size) => }
)
}
function DialogContent(props: DialogProps) {
return (
)
}
```
### Controlled
Manage the dialog state using the `open` and `onOpenChange` props.
> **Note:** Notice how the dialog triggers automagically handle the state management via onOpenChange.
```tsx
// components/dialog/controlled.demo.tsx
'use client'
import { Box, Stack } from '@/styled-system/jsx'
import {
Button,
Dialog,
DialogCloseIconTrigger,
DialogCloseTrigger,
DialogDescription,
DialogHeading,
DialogProvider,
DialogTrigger,
type OpenChangeDetails,
} from '@cerberus/react/react'
import { useSignal } from '@cerberus/react/signals'
export function ControlledDemo() {
const [open, setOpen] = useSignal(false)
return (
setOpen(details.open)}
>
)
}
```
### Root Provider
An alternative way to control the dialog is to use the `RootProvider` component and the `useDialog` hook. This way you can access the state and methods from outside the component.
```tsx
// components/dialog/root.demo.tsx
'use client'
import { Stack } from '@/styled-system/jsx'
import {
Button,
Dialog,
DialogCloseIconTrigger,
DialogDescription,
DialogHeading,
DialogRootProvider,
Text,
useDialog,
} from '@cerberus/react/react'
export function RootProviderDemo() {
const dialog = useDialog()
return (
<>
>
)
}
```
### Lazy Mount
Use `lazyMount` to render dialog content only when first opened. Combine with `unmountOnExit` to unmount when closed, freeing up resources.
Prefer this over conditionally rendering DialogProvider — see [Conditional Rendering](#conditional-rendering).
```tsx
// components/dialog/lazy.demo.tsx
'use client'
import { Stack } from '@/styled-system/jsx'
import {
Button,
Dialog,
DialogCloseIconTrigger,
DialogDescription,
DialogHeading,
DialogProvider,
DialogTrigger,
Text,
} from '@cerberus/react/react'
export function LazyDemo() {
return (
)
}
```
### Inside Scroll
Make the content area scrollable while keeping header and footer fixed using `maxHeight` and `overflow: auto`.
```tsx
// components/dialog/inside.demo.tsx
'use client'
import { Box, Scrollable, Stack } from '@/styled-system/jsx'
import {
Button,
cerberus,
Dialog,
DialogCloseIconTrigger,
DialogCloseTrigger,
DialogHeading,
DialogProvider,
DialogTrigger,
For,
} from '@cerberus/react/react'
export function InsideScrollDemo() {
return (
)
}
const CONTENT_SECTIONS = [
{
title: '1. Acceptance of Terms',
body: 'By accessing and using this service, you accept and agree to be bound by the terms and provisions of this agreement.',
},
{
title: '2. Use License',
body: 'Permission is granted to temporarily use this service for personal, non-commercial purposes only. This is the grant of a license, not a transfer of title.',
},
{
title: '3. User Responsibilities',
body: 'You are responsible for maintaining the confidentiality of your account and password. You agree to accept responsibility for all activities that occur under your account.',
},
{
title: '4. Privacy Policy',
body: 'Your use of this service is also governed by our Privacy Policy. Please review our Privacy Policy, which also governs the site and informs users of our data collection practices.',
},
{
title: '5. Limitations',
body: 'In no event shall we be liable for any damages arising out of the use or inability to use the materials on this service.',
},
{
title: '6. Revisions',
body: 'We may revise these terms of service at any time without notice. By using this service you are agreeing to be bound by the then current version of these terms.',
},
{
title: '7. Governing Law',
body: 'These terms and conditions are governed by and construed in accordance with applicable laws and you irrevocably submit to the exclusive jurisdiction of the courts.',
},
]
```
### Outside Scroll
Make the positioner scrollable so the dialog can extend beyond the viewport.
```tsx
// components/dialog/outside.demo.tsx
'use client'
import { Box, Stack } from '@/styled-system/jsx'
import {
Button,
cerberus,
DialogBackdrop,
DialogCloseIconTrigger,
DialogCloseTrigger,
DialogContent,
DialogHeading,
DialogPositioner,
DialogProvider,
DialogTrigger,
For,
Portal,
} from '@cerberus/react/react'
import { useRef } from 'react'
export function OutsideScrollDemo() {
const contentRef = useRef(null)
return (
contentRef.current}>
Dialog Title
{(section) => (
{section.title}
{section.body}
)}
)
}
const CONTENT_SECTIONS = [
{
title: '1. Acceptance of Terms',
body: 'By accessing and using this service, you accept and agree to be bound by the terms and provisions of this agreement.',
},
{
title: '2. Use License',
body: 'Permission is granted to temporarily use this service for personal, non-commercial purposes only. This is the grant of a license, not a transfer of title.',
},
{
title: '3. User Responsibilities',
body: 'You are responsible for maintaining the confidentiality of your account and password. You agree to accept responsibility for all activities that occur under your account.',
},
{
title: '4. Privacy Policy',
body: 'Your use of this service is also governed by our Privacy Policy. Please review our Privacy Policy, which also governs the site and informs users of our data collection practices.',
},
{
title: '5. Limitations',
body: 'In no event shall we be liable for any damages arising out of the use or inability to use the materials on this service.',
},
{
title: '6. Revisions',
body: 'We may revise these terms of service at any time without notice. By using this service you are agreeing to be bound by the then current version of these terms.',
},
{
title: '7. Governing Law',
body: 'These terms and conditions are governed by and construed in accordance with applicable laws and you irrevocably submit to the exclusive jurisdiction of the courts.',
},
]
```
### Initial Focus
Use `initialFocusEl` to control which element receives focus when the dialog opens.
```tsx
// components/dialog/focus.demo.tsx
'use client'
import { Stack } from '@/styled-system/jsx'
import {
Button,
cerberus,
Dialog,
DialogCloseIconTrigger,
DialogDescription,
DialogHeading,
DialogProvider,
DialogTrigger,
Field,
Input,
} from '@cerberus/react/react'
import { useRef } from 'react'
export function InitialFocusDemo() {
const inputRef = useRef(null)
return (
inputRef.current}>
)
}
```
### Final Focus
Use `finalFocusEl` to control which element receives focus when the dialog closes. Defaults to the trigger element.
```tsx
// components/dialog/final.demo.tsx
'use client'
import { Box, Stack, VStack } from '@/styled-system/jsx'
import {
Button,
Dialog,
DialogCloseIconTrigger,
DialogCloseTrigger,
DialogDescription,
DialogHeading,
DialogProvider,
DialogTrigger,
Text,
} from '@cerberus/react/react'
import { useRef } from 'react'
export function FinalFocusDemo() {
const finalRef = useRef(null)
return (
I will receive focus when dialog closes
finalRef.current}>
)
}
```
### Context
Access the dialog's state and methods with `DialogContext` or the `useDialogContext` hook.
```tsx
// components/dialog/context.demo.tsx
'use client'
import { Stack } from '@/styled-system/jsx'
import {
Button,
cerberus,
Dialog,
DialogCloseIconTrigger,
DialogContext,
DialogDescription,
DialogHeading,
DialogProvider,
DialogTrigger,
} from '@cerberus/react/react'
export function ContextDemo() {
return (
)
}
```
### Open from Menu
Open a dialog imperatively from a `Menu` item using the `onClick` handler.
```tsx
// components/dialog/menu.demo.tsx
'use client'
import { Box, Stack } from '@/styled-system/jsx'
import { ChevronDown } from '@carbon/icons-react'
import {
Button,
Dialog,
DialogCloseIconTrigger,
DialogCloseTrigger,
DialogDescription,
DialogHeading,
DialogProps,
DialogProvider,
Menu,
MenuContent,
MenuItem,
MenuSeparator,
MenuTrigger,
OpenChangeDetails,
} from '@cerberus/react/react'
import { useSignal } from '@cerberus/react/signals'
export function MenuDemo() {
const [open, setOpen] = useSignal(false)
return (
<>
setOpen(details.open)}
/>
>
)
}
function MyDialog(props: DialogProps) {
return (
)
}
```
### Nested
Nest dialogs within one another. The parent receives `data-has-nested` and `--nested-layer-count` CSS variable for styling effects like zoom-out:
```css
[data-part='content'][data-has-nested] {
transform: scale(calc(1 - var(--nested-layer-count) * 0.05));
}
```
```tsx
// components/dialog/nested.demo.tsx
'use client'
import { Box, Stack } from '@/styled-system/jsx'
import {
Button,
Dialog,
DialogCloseIconTrigger,
DialogDescription,
DialogHeading,
DialogRootProvider,
DialogRootProviderProps,
useDialog,
} from '@cerberus/react/react'
export function NestedDialog() {
const parentDialog = useDialog()
const childDialog = useDialog()
return (
<>
childDialog.setOpen(true)} />
>
)
}
interface ParentDialogProps extends DialogRootProviderProps {
onClick?: () => void
}
function ParentDialog(props: ParentDialogProps) {
return (
)
}
function ChildDialog(props: DialogRootProviderProps) {
return (
)
}
```
### Confirmation
Intercept close attempts to show confirmation prompts, preventing data loss from unsaved changes.
```tsx
// components/dialog/confirmation.demo.tsx
'use client'
import { Stack } from '@/styled-system/jsx'
import {
Button,
ButtonGroup,
Dialog,
DialogCloseIconTrigger,
DialogDescription,
DialogHeading,
DialogRootProvider,
DialogRootProviderProps,
Field,
Text,
Textarea,
useDialog,
} from '@cerberus/react/react'
import { useSignal } from '@cerberus/react/signals'
import { ChangeEventHandler } from 'react'
export function ConfirmationDialog() {
const [formContent, setFormContent] = useSignal('')
const [isParentDialogOpen, setIsParentDialogOpen] = useSignal(false)
const parentDialog = useDialog({
open: isParentDialogOpen,
onOpenChange: (details) => {
if (!details.open && formContent.trim()) {
confirmDialog.setOpen(true)
} else {
setIsParentDialogOpen(details.open)
}
},
})
const confirmDialog = useDialog()
const handleConfirmClose = () => {
confirmDialog.setOpen(false)
parentDialog.setOpen(false)
setFormContent('')
}
const handleCancel = () => {
confirmDialog.setOpen(false)
}
return (
<>
setFormContent(e.target.value)}
formValue={formContent}
value={parentDialog}
/>
>
)
}
interface ParentDialogProps extends DialogRootProviderProps {
onChange?: ChangeEventHandler
formValue: string
}
function ParentDialog(props: ParentDialogProps) {
return (
)
}
interface ConfirmationDialogProps extends DialogRootProviderProps {
onCancel?: () => void
onClose?: () => void
}
function ConfirmDialog(props: ConfirmationDialogProps) {
return (
)
}
```
### Non-Modal
We don't recommend using a non-modal dialog due to the accessibility concerns they present. In event you need it, here's what you can do:
- set the `modal` prop to `false`
- set `pointerEvents` to `none` on the `Dialog.Positioner` component
- (optional) set the `closeOnInteractOutside` prop to `false`
Preview
```tsx
// components/dialog/non.demo.tsx
'use client'
import { Box, Stack } from '@/styled-system/jsx'
import {
Button,
DialogCloseIconTrigger,
DialogCloseTrigger,
DialogContent,
DialogDescription,
DialogHeading,
DialogPositioner,
DialogProvider,
DialogTrigger,
Portal,
} from '@cerberus/react/react'
export function NonModalDemo() {
return (
Dialog Title
Far far away, behind the word mountains, far from the countries Vokalia
and Consonantia, there live the blind texts. Separated they live in
Bookmarksgrove right at the coast of the Semantics, a large language
ocean.
)
}
```
## Guides
### Close Behavior
- `closeOnEscape={false}` - Prevent closing on Escape
- `closeOnInteractOutside={false}` - Prevent closing on outside click
For conditional control, use `onEscapeKeyDown` or `onInteractOutside` with `e.preventDefault()`.
### Conditional Rendering
Unmounting `Dialog.Root` when toggling open state can break focus, scroll lock, and cleanup. Keep the root mounted and control it with `open` / `onOpenChange`.
When you want portal content out of the DOM while closed, add `lazyMount` and `unmountOnExit` to the root.
```tsx
// components/dialog/behavior.demo.tsx
import { DialogRoot, type OpenChangeDetails, Portal } from '@cerberus/react/react'
import { useSignal } from '@cerberus/react/signals'
// ❌ Avoid
export function Dont() {
const [isOpen, setOpen] = useSignal(false)
if (isOpen) {
return (
setOpen(e.open)}
>
...
)
}
return null
}
// ✅ Prefer
export function Do() {
const [isOpen, setOpen] = useSignal(false)
return (
setOpen(e.open)}
lazyMount
unmountOnExit
>
...
)
}
```
### Z-index Stacking
Use the `--layer-index` CSS variable for z-index management of stacked dialogs:
```css
[data-part='content'] {
z-index: calc(var(--layer-index));
}
```
### Dynamic Imports
When using `lazyMount` with `lazy` or Next.js `dynamic`, wrap the imported component in `Suspense`:
```tsx
// components/dialog/dynamic.demo.tsx
import { Dialog, DialogProvider, DialogTrigger } from '@cerberus/react/react'
import dynamic from 'next/dynamic'
import { Suspense } from 'react'
const HeavyComponent = dynamic(() => import('./heavy'))
export function DynamicDemo() {
return (
Open
)
}
```
### Customizing
You can customize the `Dialog` using style props and data selectors on any slot primitve.
## Primitives
You can utilize the primitive components or the `css` prop to customize the dialog.
| Component | Description |
| ------------------------ | ------------------------------------------------------------ |
| `DialogProvider` | The main state context for the dialog. |
| `DialogTrigger` | The trigger element that opens the dialog. |
| `DialogBackdrop` | The backdrop that covers the page when the dialog is open. |
| `DialogPositioner` | The container that positions the dialog content. |
| `DialogContent` | The content that is shown within the dialog. |
| `DialogHeading` | The heading title of the dialog. |
| `DialogDescription` | The description of the dialog. |
| `DialogCloseTrigger` | The trigger element that closes the dialog. |
| `DialogCloseIconTrigger` | The trigger element that closes the dialog with an "x" icon. |
## API
### Props
The `Dialog` component is an abstraction of the primitives and accepts the following props:
#### Root Props:
| Prop | Type | Required | Description |
| ------------------------ | ------------------------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `aria-label` | `string` | No | Human readable label for the dialog, in event the dialog title is not rendered |
| `closeOnEscape` | `boolean` | No | Whether to close the dialog when the escape key is pressed |
| `closeOnInteractOutside` | `boolean` | No | Whether to close the dialog when the outside is clicked |
| `defaultOpen` | `boolean` | No | The initial open state of the dialog when rendered. Use when you don't need to control the open state of the dialog. |
| `finalFocusEl` | `() => MaybeElement` | No | Element to receive focus when the dialog is closed |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial` | No | The ids of the elements in the dialog. Useful for composition. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `initialFocusEl` | `() => MaybeElement` | No | Element to receive focus when the dialog is opened |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `modal` | `boolean` | No | Whether to prevent pointer interaction outside the element and hide all content below it |
| `onEscapeKeyDown` | `(event: KeyboardEvent) => void` | No | Function called when the escape key is pressed |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `onFocusOutside` | `(event: FocusOutsideEvent) => void` | No | Function called when the focus is moved outside the component |
| `onInteractOutside` | `(event: InteractOutsideEvent) => void` | No | Function called when an interaction happens outside the component |
| `onOpenChange` | `(details: OpenChangeDetails) => void` | No | Function to call when the dialog's open state changes |
| `onPointerDownOutside` | `(event: PointerDownOutsideEvent) => void` | No | Function called when the pointer is pressed down outside the component |
| `onRequestDismiss` | `(event: LayerDismissEvent) => void` | No | Function called when this layer is closed due to a parent layer being closed |
| `open` | `boolean` | No | The controlled open state of the dialog |
| `persistentElements` | `(() => Element \| null)[]` | No | Returns the persistent elements that: should not have pointer-events disabled or should not trigger the dismiss event |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `preventScroll` | `boolean` | No | Whether to prevent scrolling behind the dialog when it's opened |
| `restoreFocus` | `boolean` | No | Whether to restore focus to the element that had focus before the dialog was opened |
| `role` | `'dialog' \| 'alertdialog'` | No | The dialog's role |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `size` | string | No | This size of the Dialog. |
| `trapFocus` | `boolean` | No | Whether to trap focus inside the dialog when it's opened |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
#### Backdrop Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Backdrop Data Attributes:
| Attribute | Value |
| -------------- | ------------------ |
| `[data-scope]` | dialog |
| `[data-part]` | backdrop |
| `[data-state]` | "open" \| "closed" |
#### Backdrop CSS Variables:
| Variable | Description |
| --------------- | ----------------------------------------------- |
| `--layer-index` | The index of the dismissable in the layer stack |
#### CloseTrigger Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Content Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Content Data Attributes:
| Attribute | Value |
| ------------------- | ------------------ |
| `[data-scope]` | dialog |
| `[data-part]` | content |
| `[data-state]` | "open" \| "closed" |
| `[data-nested]` | dialog |
| `[data-has-nested]` | dialog |
#### Content CSS Variables:
| Variable | Description |
| ---------------------- | ----------------------------------------------- |
| `--layer-index` | The index of the dismissable in the layer stack |
| `--nested-layer-count` | The number of nested dialogs |
#### Description Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Positioner Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### RootProvider Props
| Prop | Type | Required | Description |
| ---------------------- | ----------------- | -------- | --------------------------------------------------------------------------------------------------- |
| `value` | `UseDialogReturn` | Yes | |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `immediate` | `boolean` | No | Whether to synchronize the present change immediately or defer it to the next frame |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `onExitComplete` | `VoidFunction` | No | Function called when the animation ends in the closed state |
| `present` | `boolean` | No | Whether the node is present (controlled by the user) |
| `skipAnimationOnMount` | `boolean` | No | Whether to allow the initial presence animation. |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |
#### Title Props:
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Trigger Props
| Prop | Type | Required | Description |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------- |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
#### Trigger Data Attributes
| Attribute | Value |
| ---------------- | --------------------- |
| `[data-scope]` | dialog |
| `[data-part]` | trigger |
| `[data-value]` | The value of the item |
| `[data-state]` | "open" \| "closed" |
| `[data-current]` | Present when current |
---
# Divider
Category: Layout
> Used to create visual separation between content.
## Import
```ts
import { Divider } from 'styled-system/jsx'
```
## Usage
Used to visually separate content.
```tsx
// components/divider/basic.demo.tsx
import { Text } from '@cerberus/react/react'
import { Divider, VStack } from 'styled-system/jsx'
export function BasicDemo() {
return (
FirstSecondThird
)
}
```
## Appearance
To change the appearance of the divider, use the `borderStyle` prop.
```tsx
// components/divider/appearance.demo.tsx
import { Divider, VStack } from 'styled-system/jsx'
export function AppearanceDemo() {
return (
)
}
```
### Orientation
Use the `orientation` prop to change the orientation of the divider.
```tsx
// components/divider/orientation.demo.tsx
import { Text } from '@cerberus/react/react'
import { type PropsWithChildren } from 'react'
import { Divider, HStack } from 'styled-system/jsx'
function VertDivider(props: PropsWithChildren