Migrating to v2
Version 2 works with React 18 and 19 and with the Next.js App Router. This page lists what changes and what to do about it. The documentation of v1 stays available at v1.bolio-ui.com.
While v2 is in alpha, install it with the next tag:
yarn add @bolio-ui/core@next
Requirements
- • React 18.2 or later, including React 19. React 16 and 17 are no longer supported.
- • TypeScript users need
@types/react18 or 19. - • Next.js 14, 15 and 16 are tested, with the Pages Router and with the App Router.
- • Vite 8 with React 19 and plain Node (16, 18, 20, 22 and 24, with
require,importand server rendering) are tested too. - • The package is written in ES2019 syntax (v1 was ES5). Every browser that receives updates supports it. If you must support older ones, transpile
@bolio-ui/corein your build.
Breaking changes
CssBaseline.flush and CssBaseline.flushToHTML were removed
Bolio UI used to bundle a private copy of styled-jsx, and Next.js could not read its styles, so _document had to call CssBaseline.flush().
Version 2 uses the real styled-jsx package, which Next.js already collects. In the Pages Router you can delete the custom code from _document:
// v1: pages/_document.js
class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>{CssBaseline.flush()}</Head>
...
// v2: nothing to add. Remove the call, or the whole custom _document if it was only for this.
In a custom server, use a styled-jsx registry. See Bolio UI plus Next.js.
App Router needs the styles registry
The App Router does not add styled-jsx styles to the HTML by itself. Wrap your app with StyledJsxRegistry in app/layout.js:
import { BolioUIProvider } from '@bolio-ui/core'
import { StyledJsxRegistry } from '@bolio-ui/core/next'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<StyledJsxRegistry>
<BolioUIProvider>{children}</BolioUIProvider>
</StyledJsxRegistry>
</body>
</html>
)
}
You do not need 'use client' to use the components in Server Components. The entry files of the package already have it.
styled-jsx is a dependency
The package now depends on styled-jsx (5.0.7 or later) instead of bundling a copy. If your app already uses Next.js, both share the same copy.
Package entry points
The package has an exports map. These paths work:
| Path | What it is |
|---|---|
@bolio-ui/core | All components and hooks |
@bolio-ui/core/next | StyledJsxRegistry for the Next.js App Router |
@bolio-ui/core/dist/* | Deep imports of the CommonJS build, as before. They work in Node and in bundlers |
@bolio-ui/core/esm/* | Deep imports of the ES modules build, as before. They are for bundlers: Node cannot load them as they are, so in Node import the package root or dist/* |
Other paths are no longer resolved.
Changes in behavior
- • Container no longer renders a stray
gap="0"attribute in the DOM. - • Toggle, Checkbox and Radio can be reached with the Tab key and show a focus ring. The native
inputof Toggle and Radio was hidden withvisibility: hiddenand is not hidden anymore, so a CSS selector or a test that relies on that may need a change. - • Tabs items have
role="tab"instead ofrole="button", inside arole="tablist". The content hasrole="tabpanel". - • Pagination puts its items in a list and marks the current page with
aria-current="page". - • Slider thumb has
role="slider"and answers to the keyboard. - • Tooltip opens when its trigger gets the focus and closes with Escape. Popover closes with Escape.
- • Refs: every component forwards its
refnow. See Refs and accessibility. - • Types:
Tableacceptsrefand the scale props, andContainertypes itsfluidprop. - • Button types with
-light, likeprimary-light, have a tinted background. Before they looked like the filled ones.subtleis a new variant with no background. Tag, Badge, Note, Snippet, Card and Tooltip take the samelight,subtle,ghostandfilledprops, and their default look is the same as before. - • Tooltip and Popover describe their trigger to a screen reader (
aria-describedby, oraria-expandedandaria-controlsfor a click trigger). A text trigger of a click Popover is a button that opens with Enter or Space. - • Rating is a
radiogroupwith one tab stop, and answers to the arrow keys, Home and End. - • Input links its label with
htmlFor, setsaria-invalidand is described by its error message. A clickable icon is a realbutton, so the password toggle works with the keyboard and is named Show password or Hide password. It does not render an emptycrossoriginattribute anymore. - • Tabs renders the headers of its direct
Tabs.Itemchildren on the server. Before, they appeared only after the page loaded. - • Code takes
tabs,activeTabandonTabChange, and shows the name of the file as a tab. - • Modal and Drawer are not closed by a click that comes from inside them and has no pointer press before it, like Enter or Space on a focused button. Before, that closed them, whatever the button did.
- • Checkbox.Group without
valuestarts empty and keeps its own state. Before, it froze the page in an endless render loop. - • Hooks:
useKeyboardanduseClipboardcall the handler of the latest render, where they kept the one of the first. The setter ofuseCurrentStateis the same function on every render.useBodyScrollaccepts the ref thatuseRef(null)gives with the React 19 types. - • Package:
@babel/runtimeis not a dependency anymore. Shared code is inchunk-*.jsfiles next to the components, and the deep imports keep working.
Checklist
- Update React to 18.2 or later.
- Install
@bolio-ui/core@next. - Remove
CssBaseline.flush()from your custom server or_document. - In the App Router, add
StyledJsxRegistryto the root layout. - Update
@types/reactto 18 or 19 if you use TypeScript. - Search your tests and styles for the changes listed under behavior.