# 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 ( Overview Features Pricing Overview content Features content Pricing 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 ( Overview Features Pricing Overview content Features content Pricing 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:**
    Benchmark results for the signal APIs

    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:**
    Benchmark results for the signal APIs

    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 ) } ```
    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 ( {(item) => ( )} ) } ```
    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 ( A fiery cerberus ) } ``` ## 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 (