Once UI Handbook
Vibe coding
Introduction to vibe coding
Set up your local dev environment
Essential tools for vibe coding
Build your first Once UI app
Getting started
What is Once UI?
Install & config
Page skeleton
Your first page
Upgrade from 1.8 to 2.0
Once UI without Next.js
Build and launch
How to launch a portfolio site that actually gets you hired
How to build a documentation site with MDX and Next.js
How to ship a landing page and dashboard with authentication in Next.js
How to launch a social app with Next.js and Supabase
Common patterns
Hero section
Static panel
Form layout
Highlighted card
Responsive stacking
Decoration layers
Dialog & modal
Toast & feedback
Tables & lists
Loading states
App shell navigation
Dashboard & charts
Chat & messaging
Auth & verification
Settings & split panels
Data filters & toolbar
Command palette
Pricing & plans
Accordion & FAQ
Media & uploads
Empty & error states
SEO, Open Graph, and structured data
Documentation code blocks with live preview
Carousels, galleries, and before/after comparisons
Date pickers and scheduling
Context menus and dropdown actions
Tags, chips, and multi-value inputs
Roadmap & kanban
Form inputs & controls
Progress, status & badges
Scrolling & feeds
Profile, avatars & identity
Social proof & logo clouds
Masonry & media grids
Table pagination, search & bulk selection
Banners & announcements
Footer layouts
Onboarding & first run
Waitlist & coming soon
Forms that wire themselves
Design tips
Color & surfaces
Harness overview
Row, Column & Grid
Spacing & rhythm
Icons & buttons
Reveal & motion
Theme tokens, FOUC-free init, and runtime style controls
Tooltips and hover cards
Border radius & nested corners
Bring your own icons
Agent resources
Column, not Flex
Semantics over CSS
Typography scale
Common gotchas
Block anchors
Validate generated code
Pro block registry
Match task bundles
Block updates
Block updates changelog
TrademarkTrademark
Ctrl k
Search…
Sign up
Once UI Handbook
Vibe coding
Introduction to vibe coding
Set up your local dev environment
Essential tools for vibe coding
Build your first Once UI app
Getting started
What is Once UI?
Install & config
Page skeleton
Your first page
Upgrade from 1.8 to 2.0
Once UI without Next.js
Build and launch
How to launch a portfolio site that actually gets you hired
How to build a documentation site with MDX and Next.js
How to ship a landing page and dashboard with authentication in Next.js
How to launch a social app with Next.js and Supabase
Common patterns
Hero section
Static panel
Form layout
Highlighted card
Responsive stacking
Decoration layers
Dialog & modal
Toast & feedback
Tables & lists
Loading states
App shell navigation
Dashboard & charts
Chat & messaging
Auth & verification
Settings & split panels
Data filters & toolbar
Command palette
Pricing & plans
Accordion & FAQ
Media & uploads
Empty & error states
SEO, Open Graph, and structured data
Documentation code blocks with live preview
Carousels, galleries, and before/after comparisons
Date pickers and scheduling
Context menus and dropdown actions
Tags, chips, and multi-value inputs
Roadmap & kanban
Form inputs & controls
Progress, status & badges
Scrolling & feeds
Profile, avatars & identity
Social proof & logo clouds
Masonry & media grids
Table pagination, search & bulk selection
Banners & announcements
Footer layouts
Onboarding & first run
Waitlist & coming soon
Forms that wire themselves
Design tips
Color & surfaces
Harness overview
Row, Column & Grid
Spacing & rhythm
Icons & buttons
Reveal & motion
Theme tokens, FOUC-free init, and runtime style controls
Tooltips and hover cards
Border radius & nested corners
Bring your own icons
Agent resources
Column, not Flex
Semantics over CSS
Typography scale
Common gotchas
Block anchors
Validate generated code
Pro block registry
Match task bundles
Block updates
Block updates changelog
TrademarkTrademark
Once UIDocumentationBlog
© Once UI. All rights reserved.
Built with Aveiro
Beginner Guides

Upgrade from 1.8 to 2.0

The 1.8 to 2.0 upgrade in the order that keeps your app building, with the changes that compile fine and still change what you ship.
Updated 16 days ago
Your first page
Once UI without Next.js
15 min · Upgrade · 1.8 to 2.0
Most apps are done after the codemod and the subpath installs. The steps worth slowing down for are the few that change behaviour without an error.

Before you start

Commit or stash first. The codemod rewrites files in place, and reading its diff is how you check it did the right thing. Then install the new major: Nothing builds yet. That is expected: from here on, tsc --noEmit is your checklist.

1. Run the codemod

It is a single file with no dependencies, and it is not shipped in the package. Read the dry run before you let it write: It walks .tsx, .jsx and .mdx, but not plain .ts files, so anything there is left for tsc to find. Every rename is scoped to the component it belongs to, so a fill or radius on an unrelated component survives untouched. It also follows import aliases. Run it once, on a 1.8 tree. Running it twice is a no-op, but running it over code you already wrote against 2.0 can change Skeleton widths.

2. Install only the peers you import

Charts, CodeBlock and MediaUpload moved to their own subpaths. The codemod moves the imports; installing what they need is yours, and only for the subpaths you actually use:
Subpath
Install
`@once-ui-system/core/data``recharts@^3.10.1`
`@once-ui-system/core/code``prismjs@^1.30.0`
`@once-ui-system/core/media``compressorjs@^1.3.0`
An app that imports none of them installs none of them.

3. On Next.js, change one import

If your app runs on Next, do this before anything else, because it is the one step whose absence never errors: Same component, same props, with the Next adapters already wired in. Skip it and internal links do a full page reload and images skip next/image optimisation.

The changes that don't error

These compile cleanly and still change what you ship, so give them a minute each.
  • Textarea grows by default. lines used to default to 3 and now defaults to "auto". Search for <Textarea and add lines={3} wherever you wanted the fixed box. The codemod can't decide this for you.
npm install @once-ui-system/core@2
curl -O https://raw.githubusercontent.com/once-ui-system/core/main/scripts/codemod-2.0.mjs
node codemod-2.0.mjs src --dry
node codemod-2.0.mjs src
  • Effect timing is milliseconds. RevealFx delay and ShineFx speed were the only seconds-valued props, so delay={0.2} becomes delay={200}. The codemod reports small literals and any expression like index * 0.1 instead of guessing.
  • ScrollContainer is a track now. Tiles no longer come pre-shaped as portrait cards, they paint past the edge (pass clip for the old behaviour, or a phone may scroll sideways), and a trackpad swipe no longer moves it. Dragging and the controls replace that.
  • The browser floor moved. Every colour token is oklch() now, with no fallback, so the floor is Chrome 111, Safari 15.4 and Firefox 113.
  • What tsc will catch for you

    The rest surfaces as type errors, which is the easy kind.
    ColorInput's onChange hands back the value, like every other onChange in the library, instead of an event. Property accesses such as props.isChecked aren't JSX, so the codemod can't see them, but tsc can.
    Icon names are a real type now, so a typo is a compile error instead of a blank space. Expect it to find bugs you already had. Register your own icons by augmenting the interface:
    declare module "@once-ui-system/core" {
      interface IconLibraryOverrides {
        rocket: true;
      }
    }
    LayoutProvider no longer takes a breakpoints prop. The widths are fixed at xs 480, s 768, m 1024 and l 1440, with xl as the base state above them. That is what lets a responsive Flex render the right layout on the server. If you relied on custom widths, the nearest equivalent is a media query of your own in a CSS module.

    Renames at a glance

    The codemod applies all of these, but it helps to know the new names when you write fresh code:
    Component
    1.8
    2.0
    `Dialog`, `Modal`, `DatePicker`, `DropdownWrapper`
    `isOpen``open`
    `Checkbox`, `RadioButton`, `Switch``isChecked``checked`
    `Input`, `Textarea`, `Option``hasPrefix` / `hasSuffix``prefix` / `suffix`
    `Input`, `Textarea``height``size`
    `Tag`, `Pulse``variant``scheme`
    `Media`, `Carousel`, `Swiper``fill``stretch`
    `Button`, `IconButton`, `Input`, `Textarea`, `ToggleButton`
    `radius="top-left"``corners="top-left"`
    `SegmentedControl``selected` / `onToggle``value` / `onChange`
    `ProgressBar``label``showLabel`
    `Feedback`, `Toast``icon``showIcon`
    `RevealFx``trigger``revealed`
    No component was removed. Particle is now ParticleFx, and the old name stays as an alias until 3.0. Your stylesheet imports keep working as they are, and radius still means roundness, so radius="none" is fine.

    Check yourself

    • Codemod ran once, and you read its report
    • Peers installed only for the subpaths you import
    • Next apps import LayoutProvider from @once-ui-system/core/next
    • Every <Textarea> has the height you meant
    • Effect timings are in milliseconds
    The full checklist, with every rename and the reasoning behind each change, is in the migration guide on GitHub. If something breaks on 2.0, open an issue there with your version.

    More beginner guides

  • tsc --noEmit passes