Repository navigation
@chassis-ui/react@0.2.0
Minor Changes
-
54d4178: Every polymorphic component now accepts
asChild, which makes the polymorphic API usable from
React Server Components (#23). Pass the target as a child element instead of a component
reference, and the component merges its classes, props and ref onto that element:import Link from 'next/link' import { Button } from '@chassis-ui/react/button' // In a Server Component <Button asChild variant="outline"> <Link href="/login">Log in</Link> </Button>
<Button component={Link} href="/login">failsnext buildin a Server Component under Next.js
16 with "Functions cannot be passed directly to Client Components". A component reference is a
function and can't cross the server→client boundary; it only worked before because older
next/linkexports weren't plain functions. An element crosses the boundary without trouble.
The rendered<a>gets the button's classes, andnext/linkstill handles the navigation
client-side.The child's own props win over the component's, except that class names are combined and event
handlers are chained (the component's runs first). Ifchildrenisn't exactly one element,
asChildlogs a dev warning and the component renders its default element.asChildtakes
precedence when both it andcomponentare passed.asChildis implemented once in the shared polymorphic wrapper, so it works on every component
with acomponentprop.SkeletonLoaderis the exception because it renders no element of its
own.componentitself is unchanged. -
3e3f7c1: Icons are now configurable, and the components that draw icons of their own no longer depend on
Icon.Breaking:
Iconno longer references/static/icons/chassis-icons.svgby default. With no
spriteset, it renders<use href="#name">, which works for a sprite embedded in the page. To
keep the old behavior, set the path once for the whole app:<IconProvider sprite="/static/icons/chassis-icons.svg">{children}</IconProvider>
New
IconProvider. It sets defaults for everyIconbelow it (sprite,className,font,
fontPrefixfor icon fonts generated with a prefix other thancx-) and configures the icons
this library's own components draw. Nested providers extend the one above them.Replaceable library icons.
Pagination,CarouselControlPrev/CarouselControlNext,
CarouselPlayPause,NavbarToggler, the check on a selected menu or combobox item, and a string
icononToast/Notificationnow ask for their icon by purpose:check,previous,next,
menu,playorpause. You can replace any of them with another icon name or with an element
from any icon set:import { Check, ChevronLeft, ChevronRight } from 'lucide-react' <IconProvider icons={{ check: <Check />, previous: <ChevronLeft />, next: <ChevronRight /> }}>
You can also render every icon name with your own icon component (
component={MyIcon}, set in a
'use client'module). A single instance can override its icon with the newpreviousIcon/
nextIcon(Pagination),icon(carousel prev/next controls, NavbarToggler), and
playIcon/pauseIcon(CarouselPlayPause) props. A custom icon gets the class the component
positions it by, includingdirectional-icon, so arrows still flip in right-to-left layouts. It
doesn't get.icon, whosefillwould paint over outline icon sets.Pagination's previous/next chevrons are now the outline style, matching the carousel's.
Fix:
Icon'ssizehad no visible effect when chassis-css was loaded. It set only the SVG's
width/heightattributes, which chassis-css's.iconsizing overrides, sosize={48}rendered
at 24px. It now sets--cx-icon-size, which also makes it work for font glyphs, and accepts CSS
lengths as well as pixel numbers (size="1.25rem"). -
6103315: Adds
StaticTable, a server-renderable table that uses no client JavaScript (#20). It takes the
same styling props asTable(bordered,borderless,hover,striped,sm,color,
align,responsive,stacked, pluscaption) and renders your own
<thead>/<tbody>/<tfoot>/<tr>/<th>/<td>markup as written. It has no sorting, selection
or keyboard grid navigation; those remainTable's job.Import it from
@chassis-ui/react/static-table. It's the only entry point without a
'use client'directive, so a React Server Component renders it as a real Server Component. In a
Next.js 16 app, a route rendering only aStaticTableships no client JS beyond Next's own
baseline. Links,next/linkand server-action forms in cells work as they do in any server markup.import { StaticTable } from '@chassis-ui/react/static-table' <StaticTable hover striped caption="Members"> <thead> <tr><th scope="col">Name</th></tr> </thead> <tbody> {members.map((m) => ( <tr key={m.id}><td data-cell="Name">{m.name}</td></tr> ))} </tbody> </StaticTable>
For
stackedtables, give each<td>adata-cellattribute with its label.Tablereads that
label from its column headers, but a static table has no column data to derive it from.
StaticTableis exported from the root entry too, for use inside Client Components. -
a92d1e9: Every component family is now published as its own entry point, so a page only ships the
components it actually imports (#22). The subpaths are named after the component folders:import { Button } from '@chassis-ui/react/button' import { TextInput } from '@chassis-ui/react/text-input' import { Modal, ModalBody, useModal } from '@chassis-ui/react/modal'
Until now the package was a single
dist/index.jsbehind one'use client'directive. Turbopack
(Next.js 16's default bundler) can't tree-shake unused exports out of a'use client'module, so a
page rendering oneButtonshipped every component, and the react-aria/react-stately hooks behind
them, to the browser: about 214 KB gzip of client JS on top of Next.js's own baseline, whichever
bundler was used. Importing the sameButtonfrom@chassis-ui/react/buttonships about 13 KB
under Turbopack and 11 KB under webpack. Each subpath carries its own'use client'directive, so it
works directly from a Server Component, just like the root entry.The root
@chassis-ui/reactentry is unchanged and still exports everything. Both import styles
resolve to the same shared chunks, so mixing them is safe:Modalfrom/modalanduseModalfrom
the root read the same context. Each family's hook is also exported from its subpath (useModal,
useDrawer,useToast,useNotification,usePagination).sideEffectsinpackage.jsonis now accurate. Only the entry files, the chunk that installs the
global focus-ring listener, andstyle.cssare marked as having side effects, so webpack and Vite
also drop unused components from a root import (about 16 KB for the same page under webpack). This
also fixes a latent build bug: the listener's side-effect import was being tree-shaken out of the
bundle, and it only survived becauseRangeCalendarhappened to import the same module.The
DataGrid*Propstypes are now exported from the root entry too. Before, they were only
reachable from the component's own module.