v5 #6610
Replies: 41 comments 12 replies
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
|
I am not sure how much work this needs but supporting UnoCSS (with the tailwind preset) would be fantastic. Our team is used to it (or rather some of its other presets) instead of tailwind and new projects are currently split between Nuxt UI and another component library for that reason |
|
It would be great if the As for 4. (splitting components) I hope you won't go the way of shadcn/ui where you need to include 5 components just to show a nav menu with some links. I like the approach of using props and objects for that more. It's also more typesafe, since afaik there's no way of restricting the type of a child element/component. |
|
@septatrix you can try my preset that enables Nuxt UI to work with Unocss https://github.com/lehuuphuc/unocss-preset-nuxt-ui |
|
@Blechlawine We won't go the shadcn way of course not, what I meant by splitting components is in some specific cases it would be easier to override if moved to its own component like for example the Header mobile menu (https://github.com/nuxt/ui/blob/v4/src/runtime/components/Header.vue#L178). |
|
@lehuuphuc You can make a PR to add your preset to the Community page if it's working 😊 https://ui.nuxt.com/community |
I don't want to derailt this conversation any further but just a quick question, I assume this will not stop tailwind to run and compile the styles in the background? |
This depends on your project setup. My library is a preset for the unocss config, used alongside the "wind4" preset. It adds the necessary classes for Nuxt UI to function without tailwindcss. |
|
Hi @benjamincanac I think you should also add rethinking components for layout as we discussed here: #4955 Right now |
|
Not sure if this is possible, but it would be nice to have the possibility to add new tailwind classes on the fly during execution. Currently classes not used in the code are not complied so I cannot set such a class unless it is safelisted via |
|
I see two nice to have features for this library:
|
|
Since v3, |
|
How about standalone components, like Shadcn? Why? - Certain components can be customized much more deeply depending on specific needs, customizations that aren’t possible using only props or configuration options. For example, the ColorPicker could have a horizontal hue slider instead of a vertical one, and it could also include an alpha slider. These kinds of changes aren’t possible unless you modify the component at the source level. Yes, the components can already be extracted and used as standalone components with some modifications, by removing Nuxt-specific dependencies tied to the node_modules source level, but... just an idea. |
|
I think it would be a great addition to generalize the Chat components to make them fully agnostic. Currently, I've managed to build chat UIs by using the However, for v5, we should take it a step further and consider the following improvements:
This approach keeps the core component ultra-flexible and clean, while still providing an easy bridge for developers working with popular AI SDKs. What do you think? |
|
@maximepvrt Recently I experimented with Chat components in some project and thinking about somewhat similar. It would be great if Nuxt UI will provide the generic Chat as basic set: input + message + messages components, + easy way to enhance it by adding some custom things for messages (attachment previews, charts, whatever else can be imagined, ...) - something like current AI Chat can support via custom tools. And that generic Chat components should be easily turned into AI chat by adding current Chat components - reasoning, shimmer, tool, prompt, submit. |
|
Nuxt UI is a great lib, and I’ve been using it a lot lately. For example, instead of export interface NavigationMenuItem extends Omit<LinkProps, 'type' | 'raw' | 'custom'> {
// ...
[key: string]: any;
}it could be like this, so TS knows the shape of custom fields: export type NavigationMenuItem<
TMeta extends Record<string, unknown> = {}
> =
Omit<LinkProps, "type" | "raw" | "custom"> &
TMeta & {
label?: string;
icon?: IconProps["name"];
// ...
[key: string]: unknown;
}Or maybe even cleanest way - to avoid index signature and collision with interface props, like this: export interface NavigationMenuItem<TMeta = unknown> {
label?: string;
icon?: IconProps["name"];
// ...
meta?: TMeta;
}Also it might be good idea to get rid of |
|
For the css variables it would also be great if the names were more clear if they're lighter/darker than the background. With the current names |
|
A couple of things! De-Dashboarding / thin wrappersA recent discussion with @benjamincanac in #6340 about removing the Dashboard class of components. From what I can see Resize / split-panesA resize composable would be quite cool for generalised usage:
I'm a big fan of Blender-style UIs that let you split and customise the layout tree with drag-drop/split. That'd be the fun moonshot on top for the ultimate dashboard layout system.
Mobile interactionsThere are a couple of weird mobile interactions that make it hard to build a proper web app. In particular I make use of the Sizing consistency#6581, another user mentions here that maybe badges and buttons should have aligned and additional sizes, could be a good candidate for v5 with breaking changes. Vertical orientationI'd also love vertical button orientation (mentioned in #6527), and I recently added an update to the docs for vertical tab buttons. This could also have the Theming & customisation toolingI'd love more customisation and theming tooling, previewing the templates in different themes. I made a small tailwind-colors grayscale editor because I thought light mode was a bit too colour-less. This is maybe a bit too far, but I liked shadcn's recent create-page updates or Shadcn Studio's theme customiser and presets. |
Add support for MDC syntax to UEditor.It would be nice to get also composable to alter conversion from MDC to tipTap and TipTap to MDC. |
|
Since Nuxt UI is already planning a major primitives migration (e.g. Reka UI 3), I'm curious if Vuetify 0 was evaluated as well. It is also built around a headless/composable approach, while bringing years of maturity and experience from Vuetify with complex enterprise components. |
|
@alihardan I don't think we'll be switching our primitives, the Reka UI v3 migration will be pretty smooth with some internals optimizations which should not bring too many breaking changes. Switching to something like https://0.vuetifyjs.com/ or https://ark-ui.com/ would break everything and make the transition hell like between v2 (which was based on Headless UI) and v3. |
|
I am adding my view regarding this issue/ discussion about Epic C: #6716 (comment) @benjamincanac I think instead of |
|
Mmmh the issue is https://www.tailwind-variants.org/docs/composing-components#using-the-result |
|
While experimenting with theme editor, I notice one really great slider to have would be a set of abstracted spacing and padding tokens to control how compact or spaced out everything feels. You can adjust --spacing but this is too strong a lever over too many other things, you can try it here in the 'general' menu. Shadcn abstracts it away from the user further with 'styles' in their editor https://ui.shadcn.com/create but uses underlying |
|
I would like a better time picker, the current one is very aligned to 12h formats making it hard to use in other locales (or even non-24h international english) |
|
Other component collapse opportunities: BlogPost, PageCard and Card could all be one component. Perhaps Card should stay separate that the other 2 are based off. |








Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Nuxt UI v5
No firm date yet, this tracks the direction for the next major and I'll keep it updated as things land.
v5 is a theming-led major. The flagship is Design Tokens v2, a redesign of our CSS variable system. It genuinely needs breaking changes (token restructure, default appearance and contrast shifts, scoped re-resolution), so the API renames and restructures we've long parked ride along and you migrate once, with a codemod.
The other pillar is rebasing on Reka UI v3 (unovue/reka-ui#2721), the headless foundation we're built on. It brings
useRender(which drops the internalPrimitive/asChildwrapper), per-component imports, an ESM-only build, a performance overhaul and full Shadow DOM support. Reka keeps its public component API intact, so it's mostly an internal shift for us, but it underpins the whole release and brings the new Drawer primitive that lets us dropvaul-vue.A few things stay the same on purpose. We're not going the shadcn copy-paste route, components stay upgradeable through npm. Semantic color names (
primary,secondary,success,info,warning,error,neutral) and:uislot names remain versioned public API. Numbered shade scales (--ui-color-{c}-{shade},bg-primary-600) stay too, and v5 adds role tokens on top rather than dropping them.Phase 0: non-breaking precursors (ship in v4 now, de-risk v5)
Everything here is additive and unblocks/derisks v5. Independent of the v5 cut.
:uiper-slot replace — shipped v4.9.0 (feat(theme): allow replacing slot classes with a function #6562) as a(defaults) => classesfunction form (closes 🎨 Allow replacing all UI classes with new set of classes? #4953/Cant override styles #6169). Default stays merge (replace-by-default rejected as a footgun); only a docs-wording fix (Unable to remove classes from UI elements with ui-prop overwrites #5803) remains.unstyledmode — shipped v4.9.0 (theme.unstyled, feat(module): addtheme.unstyledoption #6551).diris already plumbed (UApp→ rekaConfigProvider) and the theme is largely logical already, so this is finishing the migration: convert the remaining physical holdouts intv()theme strings (~70left/rightpositioning →start/end,text-left/right→text-start/end, ~11rounded-l/r→rounded-s/e) and replace the ad-hocrtl:patches with logical props. Then ship RTL fixtures for composite components — these surface what logical props alone don't fix (icon/chevron mirroring, directional animations; some already handled viartl:-rotate-90/-rtlkeyframes). Additive (identical in LTR). De-risks the Epic Bleft/right→start/endprop-value rename.componentDetectionin the Vue package — PR feat(vue): supportexperimental.componentDetection#6731 (open, relates Everything gets loaded on a fresh app #6296, stacked on perf(vue): skip rewriting unchanged templates #6730). Ports the theme-CSS tree-shaking (experimental.componentDetection) to the Vite/Vue unplugin build; it was Nuxt-only, so the Vue build always emitted@source "./ui"and every app shipped CSS for all ~163 theme files. Detection scans the Vite root and narrows@sourceto used components + deps (single-UButtonapp: 179 KB → 45 KB, 75%). Additive (opt-in, off by default; Nuxt output byte-identical, zero snapshot changes). De-risks the Epic E flip to on-by-default across Nuxt + Vue; live HMR re-detection deferred to a restart-only note (→ Epic E follow-up).Epic A: Design Tokens v2 (flagship · breaking)
Full token system, freed from additive-only. Three tiers (primitive → theme → calculated),
-foregroundpairing convention, derive-don't-scatter (color-mixrecipes generated once per color, not/10//75literals across 150 themes).A1: Per-color roles (fixes #1284, #6588, #3307)
src/runtime/plugins/colors.ts(≈7 colors, one place):src/templates.ts@theme inline:--color-{c}-foreground/-muted/-soft/-hover→text-{c}-foreground,text-{c}-muted,bg-{c}-soft,bg-{c}-hover.src/theme/*.ts:solid:text-inverted bg-{c} hover:bg-{c}/75→text-{c}-foreground bg-{c} hover:bg-{c}-hoversoft/subtle:text-{c} bg-{c}/10→text-{c}-muted bg-{c}-softghost/link:text-{c}→text-{c}-mutedneutralinto the same role-token shape so it stops being a bespoke class set (bg-inverted/bg-elevated/ring-accented/text-default/text-muted, duplicated across ~30src/theme/*.ts). Neutral keeps its own role values (solid--ui-neutral→bg-inverted,-soft→ elevated surface,-muted→text-muted,-foreground→text-inverted) socolor="neutral"stays byte-identical, but every variant now reads the same tokens as the chromatic colors. This is what unlocks neutral-as-primary (feat(module): supportprimary: 'neutral'in app config #6608, closed): with one shared shape,primary: 'neutral'(any alias →neutral) just points that alias's role tokens at neutral's and full cross-variant parity falls out. Not doable in v4 — chromatic variants are opacity blends of one color (bg-{c}/10,ring-{c}/50) while the neutral variant is distinct solid surface tokens, so a pure--ui-primaryswap only matchessolidand outline/soft/ghost drift (why feat(module): supportprimary: 'neutral'in app config #6608 was closed rather than shipping half-parity).--ui-warning-foregroundto a dark value (yellow needs dark text). Open question 2: explicit token vs autooklch(from var(--ui-{c}) …)luminance contrast.before:bg-elevated/50(≈1.03:1, also the kbd focus indicator) in the 10 themes that use it (listbox/select/input-menu/command-palette/dropdown-menu/context-menu/navigation-menu + editor/content/prose themes). Open question 3: accent fill (bg-{c}+text-{c}-foreground, native-select pattern) vs neutral + persistent non-color cue. (Neutral fill alone can't hit 3:1 / SC 1.4.11.)UButton,UBadge, … colors on colored backgrounds #3307 on-colored-surface — a solid colored surface (Banner/PageCTA) re-scopes--ui-text/--ui-text-mutedto the color's foreground for its subtree (Material "on-primary"); removes the need for areversecolor.A2: Surface / Overlay / Elevation
--ui-bg*ramp into a re-scopable one keyed by[data-elevation="N"]. Level 0 == today (byte-identical). Generate scopes (don't hand-write) incolors.ts; ladders are neutral-index offsets:bg white→50→100→200,muted 50→100→200→300,elevated 100→200→300→400,accented 200→300→400→500bg 900→800→700→600,muted 800→700→600→500,elevated 800→700→600→500,accented 700→600→500→400color-mixadditive variant kept as documented opt-in.--ui-bg-overlay(lightwhite+ real shadow; darkneutral-800, lighter than surface). Floating components (DropdownMenu/Popover/Tooltip/Modal/Slideover/ContextMenu/CommandPalette) setdata-elevation="1"/ consume it on content root.--ui-shadow-elevatedwith top inset hairline in dark; map--box-shadow-elevated→shadow-elevated. Also expose a general--ui-shadow-*scale bridged to--shadow-*(mirrors Tailwind's current defaults, retunable in one place) — what docs(theme): exploring a full theme customizer #6675's shadow axis consumes instead of static class bundles.elevationprop on container components (Card etc.): atvvariant{0,1,2,3}writing:data-elevation. Likely a shared composable/mixin so every surface component gets it. Escape hatch:data-elevation="1"on any element.A3: Motion tokens
--ui-ease-*curated curves (incl. Apple-style--ui-ease-fluid: cubic-bezier(.32,.72,0,1)),--ui-animate-*named animations (skeleton/caret-blink/spin-fast) + keyframes,--ui-skeleton-animationtoggle, tooltip delay tokens. Bridge to--ease-*/--animate-*.data-reduce-motionglobal — extend themotion-reduce:variant to honor bothprefers-reduced-motionanddata-reduce-motion="true"(natural<UApp :reduce-motion>prop). Supersedes coarsetheme.transitions. (Additive, but ships in A3 alongside the motion tokens rather than Phase 0 — see resolved open question 1.)A4: Structural tokens + primitives + vibrant
--ui-disabled-opacity,--ui-border-width,--ui-ring-offset,--ui-backdrop(tokenized),--ui-cursor-*,--ui-font-heading/--ui-font-body+ afontSizescale.--ui-prose-flow/--ui-prose-leading/--ui-prose-size+--ui-prose-scale-ratio, so a context retunes from one wrapper (compact chat vs docs) instead of an ~11-lineprose:uiblock (shadcn-typeset-inspired, on our component model — nonot-typesethatch). v5, not v4: it only pays off with a clean modular scale as the new default. Retrofitting onto today's hand-tuned values gives ugly coefficients (*2.4,*1.6,*0.3) and asizeknob that can only rescale, not reshape (couldn't flatten a chat's heading scale). Reshaping the default scale is a default-appearance shift → breaking. Consumes the--ui-font-heading/-bodytokens above; a density API (presets / a<Prose>primitive, vs adisplay:contentswrapper trick) rides Prose × Comark (Epic C).--ui-snow/--ui-eclipse(near-white/black) as softer defaults than#fff/#000.A5: Scoped re-resolution (fixes #5657)
--ui-{c}at point of use (today baked at:rootand inherited resolved). Touchescolors.ts,templates.ts, docs FOUC scripts. The breaking architectural change.A6: Theming surface
[data-theme]var blocks + theme generator + documented stable token contract +llms.txt. Switching is already doable by hand today at the alias layer (redeclare--ui-primary,--ui-bg, … under[data-theme='x']and toggle the attribute — worth documenting now); scale-var overrides need A5, and the v5 item is generating the blocks from config.muted/dimmed/elevateddon't signal lighter-vs-darker. We're restructuring tokens anyway, so it's the moment — but weigh against the "don't churn names" guardrail; only rename behind an alias + codemod.Epic B: Prop renames (breaking · codemod-able)
links/actions→buttons(PageCTA/Empty/Banner/…)input/searchInput→filter(CommandPalette, SelectMenu; feat(DropdownMenu): addfilterprop #6153) — aligns with thefilterprop DropdownMenu/ContextMenu already haveleft/right→start/endprop values (table pinning, chat-messageside, footer, page)type: 'label'items — label font size one step smaller than items like the other menus (e.g.text-xsatmdlike Select/SelectMenu, instead oftext-smtoday)NavigationMenuItem<TMeta>/DropdownMenuItem/SelectMenuItemetc. carry a typedmeta/generic instead of[key: string]: any; dropany→unknown. Breaking type-signature change.Epic C: Component restructures (breaking)
base→slots.base([Feature]: Standardize structural slot naming (root, wrapper, container) within the ui prop #6716, closed) — convert the ~14 user-facing single-slot components (Container/Form/Kbd/Link/Main/Skeleton/FieldGroup/Page*/BlogPosts/PricingPlans/ChatShimmer, plus theprose/*set) from a top-levelbasetoslots: { base }, matching Button/Badge. Every component becomes multi-slot, so all of them gain auiprop (single-slot ones only acceptclasstoday) with one consistentapp.config.uishape.root(outer wrapper, 73 components) stays semantically distinct frombase(primary element — they coexist in ~13 components), so we don't merge them. Breaking (app.config.ui.<c>{ base }→{ slots: { base } }, variant valuesstring→{ base: string }); codemod-able.uivalues —{ class, attrs }per slot (Add "pt" props in all components , just like https://primevue.dev/passthrough #6725) — let auislot value be an object carrying arbitraryattrs(aria-*/data-*/…) alongsideclass, not just a class string, so users can set attributes on any internal element without a separate PrimeVue-styleptprop. Reuses the existing slot-key contract (no new API); additive / v4-able (string stays the class shorthand); attributes-only (noon*→ keeps styling ≠ behavior). Cost is broad not deep: every internal element binding routes through a sharedv-bindhelper. Sharp edges: precedence vs component-owned attrs (reservedata-slot/role/Rekaaria-*), and keys that forward to sub-components (leadingAvatar→UAvatar) vs plain elements. Replaces the deferred "richer:ui" note.data-slotwith the component name — every component emits genericdata-slot="root"/"title"/"label"(935 across 100 files), so[data-slot="root"]matches every component; prefix the name (data-slot="button-leadingIcon",data-slot="card-root") — alt: split intodata-component+data-slot(PrimeVue'sdata-pc-name/data-pc-section) — so each hook is globally unique and CSS-targetable per component. Pairs with richeruiabove and the Add "pt" props in all components , just like https://primevue.dev/passthrough #6725 "reach internals" ask. Breaking for bare-name selectors → v5; codemod can't rewrite user CSS, so it ships as a documented rename.sizeonModal/Card) instead of forcing a custom wrapper; aligned across components (dovetails with the Button/Badge size-scale realignment below). Purely-additive one-off variants can still land in v4 as normal feature work; the cross-component consistency is the breaking part. (moved from Phase 0.)NavigationAccordion(vertical) + mergeContentNavigation; collapsed via CSS transitions (decouple visual vs behavioral collapse, drop manual:collapsedwiring)Dashboardcategory (feat(Sidebar): add resizable functionality #6340, @mikenewbon) —Sidebar(feat(Sidebar): new component #6038) already covers the core; fold the rest into general components and delete all 10UDashboard*:DashboardSidebar→Sidebargainsresizable(minSize/maxSize/defaultSize/collapsedSize), persisted size/collapsed state (cookie/localStorage) and routeautoClose, built onuseResizable(revisits PR feat(Sidebar): add resizable functionality #6340).DashboardGroup→ gone: shared open/collapsed state moves to auseSidebar()composable (same shared-composable pattern asuseOverlay), storage defaults come from<UTheme :props>(feat(Theme): override component prop defaults #6031), and thedashboard:*Nuxt hooks are removed. Covers the old "single root div + provider context rework" item.DashboardPanel/DashboardResizeHandle→ a Splitpanes-style<UResizable>/<UResizablePane>/<UResizableHandle>on a public headlessuseResizablecomposable (min/max, direction, keyboard a11y, persistence); the panel header/body/footer chrome becomes a docs pattern.DashboardNavbar+DashboardToolbar→ one genericToolbar(keeps thetitle/icon/toggleconveniences so the migration is mostly a rename).DashboardSearch+ContentSearch→ one genericSearch(Modal + CommandPalette + shortcut).DashboardSidebarToggle/DashboardSidebarCollapse/DashboardSearchButton/ContentSearchButton→ dropped — they're one-lineUButtonrecipes onuseSidebar()/useSearch(), documented instead of shipped.BlogPosts→PageGridand other thin wrappers get the same review.wrapper= title+description,actionsslot) — PR feat(Sidebar): new component #6038. The props already match (title/description/close/closeIcon+actionsslot everywhere); the remaining gap is layout — Modal/Slideover position the close button absolutely while Drawer/Sidebar put it in an in-flow actions row (Empty div on Slideover when title or description props are not used #5336).Drawerto Reka UI's new Drawer primitive (feat(Drawer): add Drawer primitive unovue/reka-ui#2689 — Vue port of BaseUI Drawer: 4-side swipe, snap points, nested drawers, modal tiers,update:openreason details) and drop thevaul-vuedependency. API shifts (props/emits, snap-point format) → breaking. Dovetails with the unified-header work above and the Reka-base/dep-weight reduction (Epic E).outlinehas bg-default,solidrenders as inverted/dark #6515:outlineshouldn't have bg,solidshouldn't be inverted) — the two are already consistent with each other today (PageCard just addsghost/naked+ orientation/spotlight extras), so this is a redefinition of the shared variants, not a merge.squareelements (equal x/y padding) for icon-only / collapsed-sidebar alignmentsmrenders smaller than Buttonxstoday, forcing customxxsbuttons); net-new sizes are v4-able (normal additive work), the cross-component realignment is the breaking part (see consistent variant coverage above)ColorPickerto Reka'sColorArea/ColorSlider(shipped in reka-ui 2.x, currently hand-rolled) — opens the door to an alpha slider + horizontal hue layout asked for in the thread.useFilter(reka-ui backed) already powers SelectMenu/InputMenu/DropdownMenu; CommandPalette is the last Fuse consumer. Open question: fuzzy scoring + match highlighting, whichuseFilterdoesn't do.ChatMessage/ChatMessagesfrom hardcoded Vercel AI SDK types; ship a mapping helper (AI SDK → Nuxt UI message) + a generic chat base (input/message/messages) enhanceable into the AI chat (reasoning/shimmer/tool/prompt) (Normal chat message component without AI,chat person to person #6598)Contentcategory / own our content types — same move as agnostic Chat above, and the same framework-agnostic driver as Prose × Comark: with v5 leaning on Comark (which works with Vite, not just Nuxt), the Content components must not be Nuxt-specific either. The entire@nuxt/contentcoupling is two imported types (ContentNavigationItem,TocLink) across 6 files, so we define our own (structurally compatible, thin mapping helper for the rest) and drop the@nuxt/contentpeer — it becomes a pure data source:ContentNavigation→ merged into the newNavigationAccordion(see above).ContentToc→PageToc,ContentSurround→PageSurround(codemod renames).ContentSearch/ContentSearchButton→ the genericSearch(its search fn/result types are already ours).Prosecomponents on the@nuxtjs/mdc→ Comark (https://comark.dev) migration. v5 fully embraces Comark, and since Comark works with Vite (@comark/vue) as well as Nuxt (@comark/nuxt), the prose components must carry no Nuxt-specific or@nuxtjs/mdc-specific behavior. They already own their types (zero MDC imports), so it's mechanics:mdc-unwrap→ nativeunwrapslot attr, heading anchor generation drops the deprecatedruntimeConfig.public.mdc.headings.anchorLinksfallback — control moves entirely to the framework-agnosticanchorprop (shipped in v4), which defaults off in the Comark world (Comark has no global anchor config): opt in per heading, or scope a subtree via<UTheme :props="{ prose: { h2: { anchor: true } … } }">/app.config. Module gating swaps@nuxtjs/mdcfor@comark/nuxt(which already auto-registers our prose components), and the deprecatedmdcoption is removed.Editormarkdown support can then ride Comark's planned TipTap extension (first party TipTap extension comarkdown/comark#164).Epic D: Defaults & behavior (breaking)
--ui-container80rem → 90rem (default content width; layout shift)expanddefault →false(stacked)Epic E: Foundation & install contract (breaking)
componentDetectionon by default (Nuxt + Vue) — once the Phase 0componentDetectionVue port lands (feat(vue): supportexperimental.componentDetection#6731), flip theme-CSS tree-shaking on by default so apps only ship CSS for components actually used. Breaking-ish: dynamicresolveComponent(name)usage can be missed (escape hatches stay — thecomponentDetection: [...]allowlist, orfalse). Also promote the Vue dev-server HMR re-detection (restart-only in feat(vue): supportexperimental.componentDetection#6731) from "manual-validation follow-up" to a supported path here.useRender(drops the internalPrimitive/asChildwrapper), per-component subpath imports, ESM-only build, plus the perf overhaul and Shadow DOM support. Reka keeps its public component API intact, so it's mostly internal for us; the ESM-only + subpath-import parts touch our build/install, which is why it rides with the v5 cut. Dev-server payoff (Everything gets loaded on a fresh app #6296): the per-family subpath imports (feat(core): remove barrel file in favor of single exports unovue/reka-ui#2764) mean migrating our ~132from 'reka-ui'barrel-import files, and theSymbol.forcontext keys (fix(shared): create context withSymbol.forto fix duplicates unovue/reka-ui#2763) let us droptranspile: ['reka-ui'](src/module.ts, 0.8–1.2s first-request win) — both pulled forward into v4 as small PRs if they ship in a reka-ui 2.x release before the v3 cut.@tiptap/*+ 13 transitiveprosemirror-*, Editor-only) and embla (7 pkgs, Carousel-only) fromdependencies→peerDependencies+peerDependenciesMeta.optional.reka-uistays hard dep.npm icommand; document per-component install requirements.vaul-vueentirely — Drawer moves to Reka's native Drawer primitive (Epic C / feat(Drawer): add Drawer primitive unovue/reka-ui#2689), one fewer external dep on top of the Reka base./vueinstall slugs →/vite(docs-only, non-breaking, v4-able) — the non-Nuxt path is really the Vite plugin (@nuxt/ui/vite+vite.config.ts), not "any Vue setup", so the installation +icons/color-mode/i18nintegration pages read better at/…/vite(pairs as "Nuxt module vs Vite plugin"). Keep the switcher label "Vue" (audience) +framework: vuefrontmatter — only the URL slug changes; ship 301s so old URLs keep working. Catch: the framework value doubles as the slug in the toggle's route-swap watcher (docs/app/pages/docs/[...slug].vue), so it needs a small{ nuxt, vue → vite }slug map; ~75 inbound links to sweep (mostlyicons/vue#themecomponent tips). The switcher label itself stays "Vue" — Vue UI libs organize by framework, not build tool.Epic F: Migration & rollout
app.config.uislot-shape migration ({ base }→{ slots: { base } }).llms.txt.Epic G: Launch (landing redesign + live theme playground)
main.css+app.config.ts/vite.config.ts. Productizes the Epic A6 theme generator and is the canonical, interactive demo of the token system. Already in the works: docs(theme): exploring a full theme customizer #6675 (@mikenewbon) prototypes the full studio on top of the docsThemePicker/useThemework — palette bezier-curve editor, shadow/border style axes, semantic shade controls, presets + live preview, minimal-override exports — heading toward a dedicated HeroUI-style/themeslayout. v5 extends it to the new token surface.Sequencing
unstyled[done], RTL, VuecomponentDetectionport; plus deprecation aliases as they land).Branching & release
v5branches fromv4, cut only when the first breaking change is ready — Phase 0 stays inv4, so nothing diverges before then.v4; breaking PRs →v5.v4→v5frequently (after eachv4release, or weekly) and never the reverse. Usegit merge, not rebase —v5is a shared branch, so no force-push.v4, the forward-merge carries it intov5, and thev5PR is just the removal.v4stays the default branch + npmlatestuntilv5is close;v5publishes prereleases (5.0.0-alpha.x) to thenexttag. CI on both. Flip the default tov5near release.colors.ts/templates.ts/src/theme/*, whichv4keeps touching too. Land additive token scaffolding inv4first, keep the breaking theme edits in tight batches, and merge often so conflicts stay small.Open questions (feedback welcome)
Motion tokens — ship in v4 (Phase 0) or with Epic A?Resolved: ship with Epic A (A3) — inert in v4 without adoption, and keeps token churn off the v4↔v5 conflict files. (Color-roles + surface/overlay + indirection-flatten + container stay v5 regardless.)-foreground— explicit token + defaults vs autooklch(from …)luminance.defaultVariants(surfaced by docs(theme): exploring a full theme customizer #6675) — theme presets want to flip default variants at runtime, possibly per component group (buttons vs cards vs inputs); todaytheme.defaultVariantsis build-time only.--ui-bgfollow the neutral ramp instead of pure white (also surfaced by docs(theme): exploring a full theme customizer #6675)? Interacts with the A2 elevation ladders.Not adopting / deferred
<UDefaults>provider — redundant:<UTheme :props>/<UTheme :ui>already give scoped, deep prop + slot-class defaults via provide/inject. Implicit contextual defaults (auto-restyle based on the ancestor component) intentionally skipped as too magic.--ui-field-*) —app.config.ui.input/<UTheme :ui>already restyle form controls globally and type-safely; a shared "all inputs at once" override is marginal and adds token surface.data-scrollbar— nice polish but orthogonal to the design-token story; could ship as a tiny standalone feature anytime, not a v5 pillar.unstyled+ the(defaults) => classes:uireplace, with Reka UI underneath for full headless control.<dialog>refactor, TSisolatedDeclarations— internal/DX, evaluate separately; not v5 blockers.Issue index
#1284 #6588 #3307 (contrast) · #6608 (neutral-as-primary, closed) · #5657 (scoped theming) · #6515 (variant naming) · #6153 (filter) · #5803 (
:uireplace) · #5336 (close buttons) · #4953 #6169 (per-slot replace) · #6038 (unified headers) · #6551 (unstyled) · #6576 (focus rewrite, shipped) · #4955 (layout components) · #6340 (de-dashboard) · #6598 (chat) · #6411 (variant coverage) · #6716 (slot naming, closed) · #6725 (pt→ richerui, closed) · #6675 (theme studio prototype) · #6581 (size scales) · #6293 #6296 (perf) · #6731 (componentDetection Vue port) · unovue/reka-ui#2721 (Reka UI v3) · unovue/reka-ui#2689 (Drawer primitive → drop vaul-vue) · unovue/reka-ui#2763 (Symbol.for contexts) · unovue/reka-ui#2764 (subpath imports) · https://comark.dev (Comark)Feedback
Feedback on any item above is very welcome, ideally anchored to a specific one (the open questions especially). Please keep feature requests and new component requests out of this thread, since those aren't tied to a major and can ship in v4. As items firm up I'll spin them into their own issues and PRs.
All reactions