2.0 alpha
These docs describe 2.0. Install it with @once-ui-system/core@alpha.
On 1.8.x? Read the 1.x docs
TrademarkTrademarkDocs
Ctrl k
Search docs...
Contexts

AdapterProvider

Updated: August 31, 2026
View on GitHub
Core no longer imports next/* at runtime. Four things a framework owns — rendering a link, rendering an image, reading the current path, and navigating — are read through adapters instead, and the defaults are plain DOM. That is what lets Once UI install and run in a Vite app, a Remix app, or a plain React app with no Next.js present. Everything that routes or loads an image goes through this seam: SmartLink, ElementType — and so any Button, Card or ToggleButton with an hrefMedia, Logo, MegaMenu and Kbar. Import LayoutProvider from @once-ui-system/core/next instead of the root. That is the entire migration: same props, same tree, and the Next adapters (next/link, next/image, next/navigation) are installed for you. Miss this and nothing crashes — which is the trap. The DOM defaults take over, so internal links become full page loads and images stop being optimized. The app still works, just worse. If routing suddenly feels heavy after upgrading, this import is the first thing to check. Apps that compose their own provider tree can keep using NextAdapterProvider directly; LayoutProvider from /next only removes the boilerplate. Do nothing. The defaults render <a> and <img>, read window.location.pathname (popstate-aware, "" on the server) and navigate with window.location.assign. Pass adapters to AdapterProvider. It is a partial override merged onto whatever the nearest provider already supplies, so you can replace only the piece your framework does differently. Because providers merge rather than replace, nesting one inside another is a legitimate way to override a single adapter for one subtree. The obvious wish is for core to notice Next.js and wire itself up. It cannot, and the reason is worth knowing before you try it in your own app. Two of the four adapters are hooks, and the two implementations call different numbers of hooks: the DOM useNavigate returns a closure and calls none, the Next one calls useRouter and useCallback. Swapping them after mount raises "Rendered more hooks than during the previous render." So resolution has to settle before the first render — and a browser bundle has no synchronous way to conditionally resolve an optional module. An explicit import is the bundler-independent version of the same thing. useAdapters() returns the resolved set. Use it when you are building a component of your own that should route the same way core's do.
AdapterProvider
Type
Default
adapters
Overrides. Anything omitted keeps the value from the nearest provider, or the DOM default.
Partial<OnceUIAdapters>
children
React.ReactNode
Adapter
Type
Default
Link
Renders internal links.
ComponentType<AdapterLinkProps><a>
Image
Renders images. Lazy unless priority is set.
ComponentType<AdapterImageProps><img>
usePathname
Current pathname. Popstate-aware; "" on the server.
() => stringwindow.location.pathname
useNavigate
Imperative navigation.
() => (href: string) => voidwindow.location.assign
The image adapter's contract follows next/image semantics so the Next implementation is a pass-through rather than a translation layer.
Prop
Type
Default
src
Image source. Required.
string
alt
Alternative text. Required.
string
fill
Absolutely fill the nearest positioned ancestor.
booleanfalse
priority
Eager-load and raise fetch priority.
booleanfalse
unoptimized
Skip framework image optimization. No-op for the DOM default.
booleanfalse
width
number
height
number
Edit this page on GitHub
TrademarkTrademark
Built with curiosity by Lorant One and the Design Engineers Club