Migration to v6
Track and review changes to the CoreUI for React.js components to help you migrate from v5 to v6.
CoreUI for React is the React.js version of the CoreUI components library, so before reading this migration guide, please also check the CoreUI 5 to 6 migration guide — it covers the markup, class names, Sass architecture and design tokens this release is built on. This page covers what changes in the React API.
Dependencies
- Breaking
propTypesare gone from every component, andprop-typesis no longer a dependency. Prop validation is the TypeScript types, which every component has carried for years — the runtime copy only duplicated them, and React 19 ignores it anyway. If you were readingSomeComponent.propTypesto introspect a component, read the exportedSomeComponentPropstype instead. - Breaking React 18 is the minimum.
peerDependenciesmoved from>=17to>=18. The library already useduseId, which React 17 does not have, so the old range promised support it could not deliver. React 19 is supported. - Breaking Popper is gone; positioning runs on
Floating UI.
@popperjs/coreis replaced by@floating-ui/dom. Update your lockfile — nothing else is required for the default configuration. - Breaking
popperConfigis nowfloatingConfigonCDropdown,CPopoverandCTooltip, and it takes a Floating UIComputePositionConfiginstead of a Popper one. Thepopperboolean prop stays and still switches static positioning. - Breaking Positioned elements are stamped with
data-coreui-placement, notdata-popper-placement. Custom CSS, tests or scripts keyed on the old attribute must be updated. The v6 stylesheet draws tooltip, popover and menu arrows from this attribute, so it is not optional. - Breaking The exported hooks are renamed:
usePopper→useFloating,useDropdownWithPopper→useDropdownWithFloating.
New components
-
CCombobox— a select-like control: a.form-controltoggle next to a popup List box, with single and multiple selection, a search field with optional accent-insensitive matching, sections, a checkbox indicator and a hidden select that posts the value and takes part in form validation. Ships with theuseCombobox()hook. See the Combobox docs. -
CDateInput,CTimeInput— section fields: a masked, keyboard-first input where every part of the value (day, month, year, hour…) is its own editable section.<CDateInput type="datetime">carries the time too. The pickers are built on them, and they work standalone. -
CDialogandCDrawer— the native-<dialog>popup and side panel with the Bootstrap 6 look and thedialog-*/drawer-*classes.CDialogis a centered floating panel over a blurred backdrop with optional slide-in variants;CDraweris a floating, inset, rounded panel with sheet, translucent, fit-content and fullscreen variants plus responsive breakpoints.CDialog’sfullscreentakes a breakpoint as well astrue, likeCModal’s.They share their behavior with
CModalandCOffcanvas, which keep the classic look and the v5 vocabulary — and one callback vocabulary of their own:onShow,onCloseandonClosePreventedon both, where the legacy pair saysonHide.CModalandCOffcanvasare deprecated and removed in v7 — preferCDialogandCDrawerfor new work. Pick one per surface; they are independent components. -
CListBox— a keyboard-driven list of values on the ARIA listbox pattern, withnone/single/multipleselection, sections, descriptions, links, an empty state and anactiveDescendantmode that leaves the focus in your own input. Ships withCListBoxOption,CListBoxSection,CListBoxHeader,CListBoxSelectAll,CListBoxTitle,CListBoxSubtitle,CListBoxEmptyand theuseListBox()hook. See the List Box docs. -
CMenu— contextual overlays with submenus, container teleport, responsive placement and logical (start/end) placements. Ships withCMenuToggle,CMenuContent,CMenuItem,CMenuHeader,CMenuDivider,CMenuText,CSubmenuand theuseMenu()hook. See the Menu docs. -
CNumberInput— a number field with a stepper you can hit, in place of the browser’s own spinner (tiny in some engines, absent on iOS).min,maxandstepstay native. -
CTransfer— two lists and four buttons that move values between them, with a select-all header and counter, an optional per-list search, a one-way mode and an announcement of every move throughannounce(). Composes twoCListBoxes and ships with theuseTransfer()hook. See the Transfer docs.
Field components
CAutocomplete, CMultiSelect, CDatePicker, CDateRangePicker and CTimePicker are built on
one frame and one panel. These two changes apply to all five; the per-component sections below
cover what is specific to each.
-
Breaking A field component is the frame. It carries
.form-control-groupon its own root instead of nesting one, so a field is a direct child of an.input-grouplike any other control and there is no wrapper level to reach through. Custom CSS written as.date-picker .form-control-groupno longer matches anything. On the pickers, the label and the validation feedback also move out of the frame.The frame replaces the per-component shells:
.autocomplete-input-group,.form-multi-select-input-groupand their-sm/-lgvariants are gone, the adornments are.form-control-cleaner/.form-control-action(replacing.autocomplete-cleaner,.form-multi-select-cleaner,.autocomplete-indicator,.form-multi-select-indicator), and thesizeprop puts.form-control-sm/.form-control-lgon the frame itself. -
Breaking The options/picker panel exists only while it is open. It is built when the field opens and removed once the exit transition has played, so a closed field carries no panel markup at all — code that queried
.combobox-popupor.date-picker-popupup front, or held a ref to it across a close, needs to look it up per open.
Theme colors
Breaking light and dark are replaced by inverse.
They were theme colors named after a color scheme and neither followed one: <CButton color="dark">
stayed near-black on a near-black page. There are seven colors now — primary, secondary,
success, info, warning, danger, inverse — and the Colors type lists them, so
color="dark" and color="light" no longer type-check against the union.
-
color="dark"becomestheme="inverse".inverseresolves to gray-900 in light mode and gray-100 in dark, so it stands against the page in both. -
color="light"becomestheme="secondary".secondaryis the light neutral now (gray-100 / gray-600) rather than the mid gray-blue#6b7785, so components already using it change appearance. -
Where the class was painting a plain panel rather than naming a color, reach for
bg-4orbg-3; where it was a fixed dark backdrop, setdata-coreui-theme="dark"on the container. -
Breaking
coloron buttons is deprecated in favour oftheme. A v6 button is a shape plus a colour:variantpicks the shape (solid,outline,subtle,ghost) andthemepicks the colour.colorrenders the v5 spellingbtn-{color}, which fuses the two and cannot inherit a colour from an ancestor; it still works and is removed in v7.<CButton color="primary" />becomes<CButton theme="primary" />, and<CButton color="primary" variant="outline" />becomes<CButton theme="primary" variant="outline" />.color="link"becomes thelinkproperty — a link button is a shape, not a colour, and it takes its colour from the link colour rather than from a theme.color="transparent"is gone with the.btn-transparentclass it rendered — usevariant="ghost", which shows nothing but the button’s content until hover and carries the base styling on its own. -
Breaking The theme colour tokens finish their rename.
--cui-{color}-text-emphasisis--cui-{color}-fg-emphasisand--cui-{color}-border-subtleis--cui-{color}-border, matching the slot names the components already read. Custom CSS naming the old tokens has to follow; the class names (.text-primary-emphasis,.border-primary-subtle) are unchanged. -
Breaking Theme colours have no bare token.
--cui-primary,--cui-secondary,--cui-success,--cui-info,--cui-warning,--cui-dangerand--cui-inverseare gone. A theme colour is its set of slots —-base,-bg,-fg,-fg-emphasis,-bg-subtle,-bg-muted,-border,-focus-ring,-contrast— and the colour itself is-base:--cui-primary-baseis what--cui-primarywas, and-bgpoints at the same stop. A style prop or stylesheet of yours renames one to one, and setting the palette stop retints the colour:- <CBadge style={{ '--cui-badge-bg': 'var(--cui-primary)' }}> + <CBadge style={{ '--cui-badge-bg': 'var(--cui-primary-base)' }}> - :root { --cui-primary: #6f42c1; } + :root { --cui-primary-500: #6f42c1; } -
Breaking The palette is the theme colours.
$colorsholdsprimary,success,info,warning,dangerandgray; the ten hues ($blue…$cyan) and every--cui-{hue}and--cui-{hue}-{stop}token are gone, as are$secondaryand$inverse. Each$theme-colorsentry pointsbaseandbgat its palette’s500stop and the other slots at that scale;secondaryreads--cui-gray-200andinverse--cui-gray-900. No palette token is bare: the literal sits in-500, so--cui-gray-500is what--cui-graywas, and--cui-primary-500is the runtime knob for the whole family:- :root { --cui-primary-base: #6f42c1; } + :root { --cui-primary-500: #6f42c1; } - <CBadge style={{ '--cui-badge-bg': 'var(--cui-teal-600)' }}> + <CBadge style={{ '--cui-badge-bg': 'var(--cui-success-600)' }}> -
Breaking Under a theme the theme wins over a component color token. A component reads the
--cui-theme-*slot on the property and its own token only as the fallback. In v5 the color came from the component’s tokens, so a token you set on a colored alert applied; in v6 a token set on a themed component, or for the whole page in your CSS or a$…-tokensmap, changes only the components without a theme. To recolor one themed component, set the slot instead:<CAlert theme="danger" style={{ '--cui-theme-bg-subtle': '#fde2e4' }}>. -
Variants keep the component token as the override point. Button variants,
variant="solid"on an alert,subtleandoutlineon a badge,subtleon an avatar andoutlineon a chip map the component tokens to their own slots and paint from the tokens, so there a component token you set wins over the theme. Validation states win over a theme too.
Surface ladder
Breaking The neutral surfaces are a numbered ladder.
.bg-body-secondary, .bg-body-tertiary, .text-body-secondary and .text-body-tertiary are
deprecated in favor of ordinal names: .bg-1 … .bg-4 for surfaces and .fg-body, .fg-1 …
.fg-4 for the text hierarchy, with two new rungs (.bg-1, .bg-2) between the body and the old
surfaces. The deprecated names render identically and are removed in v7. The tokens moved with
them — --cui-bg-body, --cui-bg-1 … and --cui-fg-body, --cui-fg-1 … are canonical, and
--cui-secondary-bg, --cui-tertiary-bg, --cui-secondary-color, --cui-tertiary-color,
--cui-body-bg and --cui-body-color remain as aliases until v7. No component prop is affected.
The deprecated surface names sit two rungs down with the rest of the library:
.bg-body-secondary renders --cui-bg-3 and .bg-body-tertiary --cui-bg-2, while
.text-body-secondary stays --cui-fg-2 and .text-body-tertiary --cui-fg-3.
Sass
- Breaking A denser spacing scale.
$spacershas thirteen steps,0–12:.25 / .375 / .5 / .75 / 1 / 1.25 / 1.5 / 1.75 / 2 / 2.25 / 2.5 / 3 remfrom1(v5:.25 / .5 / 1 / 1.5 / 3). Every spacing, gap, space and gutter utility changes value under the same number, and so do the numericgutter,gutterXandgutterYvalues ofCRow; the v5 steps map to v6 as1→1,2→3,3→5,4→7,5→12.p-3becomesp-5,mb-4becomesmb-7,<CRow xs={{ gutter: 3 }}>becomes a gutter of5. Components keep their size, cards aside (seeCCard): their tokens read the stops that carry the v5 values.
Breaking The neutral ladder is three maps.
$theme-bgs, $theme-fgs and $theme-borders in scss/_theme.scss replace the $fg-*,
$bg-*, $border-subtle, $border-muted and $border-emphasized scalars. :root emits every
entry as --cui-bg-{key}, --cui-fg-{key} or --cui-border-{key}, and the .bg-*, .fg-* and
.border-* utilities are generated from the same maps. $border-color stays a scalar — it is the
default every component reads, not a rung. Override an entry instead of a variable:
- $bg-body: #000;
- $fg-body: #111;
+ $theme-bgs: ("body": #000);
+ $theme-fgs: ("body": #111);Breaking $enable-important-utilities and
$enable-negative-margins are gone. Utilities carry no !important and there is no switch to
bring it back — they are emitted in the last cascade layer, so they already beat every component;
if your own CSS sits outside a layer and has to lose to utilities, move it into @layer custom.
Negative margins are no longer a switch over the whole scale: the $negative-spacers map ships two
inline steps, so .me--1, .me--2, .ms--1 and .ms--2 are always available.
Breaking $grid-gutter-width is $grid-gutter-x, and
$grid-gutter-y joins it. The row gutter still defaults to 0, so <CRow> and <CCol> render
as before unless you set it; $grid-gutter-width stays as a deprecated alias of $grid-gutter-x
until v7 — set the new name, the alias only reads.
Breaking $font-sizes sets a line height per stop
instead of one value for all headings. xs is 1.25, sm … lg the body leading, xl 2.5⁄1.75,
2xl 3⁄2.25, 3xl 1.2, 4xl and 5xl 1.1, 6xl 1 — the larger the type, the tighter the
leading. The sizes are unchanged, so headings keep their size and change their leading: <CCardTitle>
and every h1–h6 follow their stop through --cui-line-height-*.
Breaking Every shadow is a stack of two to four layers
with a negative spread instead of one wide blur, so a raised surface reads as lifted rather than
smeared. Components that draw a shadow — <CDropdown>, <CModal>, <CPopover>, <CToast> — pick
this up with no change of yours. .shadow-xs and --cui-box-shadow-xs are new, and a custom
$shadows entry is now a comma-separated list of layers.
New: .w-1 … .w-12, one $spacer per step from the $sizes map, next to the percentage
width utilities.
New keys in $zindex-levels: menu, drawer, dialog, on the same values as dropdown,
offcanvas and modal, so <CMenu>, <CDrawer> and <CDialog> read their own --cui-z-* token
and .z-menu, .z-drawer and .z-dialog join the utilities. The legacy keys stay; both spellings
of a pair resolve to one number.
Breakpoints
Breaking The three largest breakpoints move and the
largest is renamed. lg is 1024px (was 992), xl 1280px (was 1200) and the top tier is
2xl at 1536px (was xxl at 1400); xs, sm and md are unchanged. <CContainer> grows
with them: 1200px at xl (was 1140) and 1440px at 2xl (was 1320).
Every prop and prop value that named the top tier is renamed. 2xl is not a valid JSX attribute
name, so the four grid-shaped props are passed with a spread:
- <CCol xxl={6} />
+ <CCol {...{ '2xl': 6 }} />
- <CRow xxl={{ cols: 3 }} />
+ <CRow {...{ '2xl': { cols: 3 } }} />
- <CContainer xxl />
+ <CContainer {...{ '2xl': true }} />
- <CPlaceholder xxl={4} />
+ <CPlaceholder {...{ '2xl': 4 }} />The props that take a breakpoint as a value change without ceremony:
| Component | Prop | Before | After |
|---|---|---|---|
CCol, CRow, CContainer, CPlaceholder | the tier itself | xxl | '2xl' |
CDialog, CModal | fullscreen | "xxl" | "2xl" |
CDrawer, COffcanvas | responsive | "xxl" | "2xl" |
CDropdown | alignment | { xxl: 'end' } | { '2xl': 'end' } |
CHeader, CNavbar | container | "xxl" | "2xl" |
CNavbar | expand | "xxl" | "2xl" |
CListGroup | layout | "horizontal-xxl" | "horizontal-2xl" |
CTable | responsive | "xxl" | "2xl" |
CMenu, CPopover, CTooltip | placement | "xxl:top" | "2xl:top" |
CIcon’s size is not a breakpoint and keeps every value it had — size="xxl" is unchanged.
Components
The visible family
-
visibleis deprecated in favour ofdefaultShow/show/onShowChangeonCAlert,CDialog,CDrawer,CDropdown,CMenu,CModal,COffcanvas,CPopover,CSidebar,CToastandCTooltip. The old prop was semi-controlled: it fed the internal state, but the component still closed itself, so the prop and reality desynchronized on the first Escape or backdrop click — a confirm-before-close dialog or a pinned tooltip could not be built on it. The new props say which mode is which:defaultShow— the uncontrolled starting point, read once on mount.show— controlled. The component renders exactly what you pass; close and trigger gestures (Escape, a backdrop or outside click, the dismiss button, hover, the autohide timer) only callonShowChange, so it has to be fed back.onShowChange— fired with the requested state in both modes.
visiblekeeps its old behaviour unchanged, is ignored whendefaultShoworshowis set, and is removed in v7. -
showreplacesvisibleon the purely controlled components too —CCollapse,CTabPane,CTabPanelandCBackdroptakeshow, andvisiblestays as a deprecated alias that loses to it. These never owned their state, so nothing changes in behaviour; the library speaks one word for the same thing. -
CSubmenutakesdefaultShow/show/onShowChangein place of itsvisible, which was a seed the submenu silently overwrote on hover; a submenu can be controlled now. -
Breaking
CNavGroupmoves todefaultShow/show/onShowChange, andshowis purely controlled — a group givenshowrenders what the prop says and the toggler only callsonShowChange, so a bareshowwithout the callback freezes the group. An expand-by-defaultvisiblemaps todefaultShow.visible,defaultVisibleandonVisibleChangestay as deprecated aliases; the function form oftogglerreceives{ show }(withvisiblestill present). -
CToasterrenders the toasts of a store as controlled — each one takesshowfrom itsopenflag, and the close button and the autohide timer go throughtoaster.close(id).
Controlled value props
Breaking The value and state props are fully controlled
when set — the same contract the visible family moved to, applied to the props that hold a
component’s value. They used to be semi-controlled: the prop fed the internal state, but the
component still wrote to it, so the prop and reality desynchronized on the first user interaction.
Now the component renders exactly what you pass, an interaction only calls the callback, and
nothing moves until you feed the change back — a bare value prop without its callback freezes the
control. Code that passed one as an initial value migrates to the new default* twin, which seeds
the uncontrolled state once, on mount:
| component | controlled prop | uncontrolled seed |
|---|---|---|
CAutocomplete, CMultiSelect | value | defaultValue |
CButton (toggle mode) | active | defaultActive |
CChip | selected | defaultSelected |
CDatePicker | date | defaultDate |
CDateRangePicker | startDate, endDate | defaultStartDate, defaultEndDate |
CPasswordInput | showPassword | defaultShowPassword (with the new onShowPasswordChange) |
CRangeSlider | value | defaultValue |
CSmartTable | sorterValue, columnFilterValue, globalFilterValue, activePage, itemsPerPage, selected | defaultSorterValue, defaultColumnFilterValue, defaultGlobalFilterValue, defaultActivePage, defaultItemsPerPage, defaultSelected |
CTimePicker | time | defaultTime |
CAutocomplete,CMultiSelect: the per-optionselectedflags are a seed, ignored entirely whenvalueordefaultValueis set, and they no longer re-apply on later renders of the same option. InCAutocompletea flag applies once, when its option first appears. InCMultiSelectit applies only to the options the component starts with: options that arrive later never change the selection, andresetSelectionOnOptionsChangeempties it and takes the marked options when the values change. Withvalueset the reset only requests the empty selection throughonChange. A native reset of the form withvalueset requests the firstvaluethroughonChange; it used to leave the controlled selection to you.- Form controls answer a native form reset the same way, controlled ones included.
CAutocomplete,CChipInput,CCombobox,CDateInput,CDatePicker,CDateRangeInput,CDateRangePicker,CFormRange,COneTimePassword,CRangeSlider,CRating,CTimeInputandCTimePickerwith a controlled value used to keep it on a reset; they now ask for the value they started with through their change callback and show your value until you take it, so a page that resets its own state from the form’sresetgets that request after its handler.CRangeSliderandCFormRangenow callonChangewhen an uncontrolled reset moves the value, andCStepperkeeps the validation marks of a form reset a listener cancels. CSmartTable: a controlled prop change no longer echoes its own callback. Passing a newsorterValueused to fireonSorterChangeback at you (and likewise for the other five); the callbacks now fire only when the user acts. The page clamp is a request in controlled mode — when the items shrink below the current page, the table callsonActivePageChange(1)instead of moving itself.CLoadingButton:loadingis the controlled mode and wins overtimeout. Whileloadingis set the button never stops the spinner itself andtimeoutis ignored;timeoutwithoutloadingis the uncontrolled auto-stop it always was.CButton: a toggle button’sactiveis controlled when set — a click only firesonClickand the pressed state follows your prop; seed an uncontrolled toggle withdefaultActive. Without either prop nothing changes.CCalendar: equal re-renders no longer reset the view. A parent re-rendering with an equalcalendarDate,startDateorendDate(a freshDateobject holding the same value) used to yank back the navigated month and cancel an in-progress range selection; only a real value change syncs now. Month names also followlocaleandmonthFormatchanges at runtime.
Validation state
Deprecated One validationState prop replaces the
valid and invalid booleans on every form control, field and picker, and on CFormFeedback.
validationState="invalid" renders is-invalid and sets aria-invalid on the control,
"valid" renders is-valid, and any custom state from the $form-validation-states Sass map
renders is-{state} — two independent flags could neither express a third state nor refuse
valid and invalid at once. On the components that had them in v5 the booleans keep working as
deprecated aliases (with both set, invalid wins, where they used to stack), and are ignored
whenever validationState is set; CCheckbox, CChipInput, CCombobox, CDateInput,
CDateRangeInput, CNumberInput, CRadio, CSwitch and CTimeInput take only
validationState. Four side effects worth knowing:
feedbackfollows the state: it renders only whilevalidationStateis set, styled after it, and describes the control in every state, not only the invalid one.- Every control that reads
validationState="invalid"now setsaria-invalid="true", exceptCCombobox, whose toggle is a button and takes only the message inaria-describedby; a control without a validation state still sets none. - Breaking
validationState="invalid"(or the deprecatedinvalid) blocks the submit onCFormInput,CFormSelect,CFormTextarea,CNumberInput,CPasswordInput,CCheckbox,CRadio,CSwitch,CFormCheckandCFormSwitch, with the text of the invalid feedback as the browser’s message, the way it already did on the fields that build their own markup. Each control callsonValidationStateChange(undefined)when the user changes its value or the form is reset; pass your state setter there (with the deprecatedinvalid, set it tofalsethere instead), or a form you gave an invalid state stays blocked until you clear it yourself. See Validation. - Breaking
CMultiSelect,CChipInput,CCombobox,COneTimePassword, the date and time fields, the pickers andCAutocompletetake and show their validation state the same way, described under Form components. A field a validation reports shows the state on the component’s frame, and<CForm>andCStepperleave the field that carries the value to the component. After taking the result over withpreventDefault()inonValidate, keepevent.isValidrather than callingcheckValidity(): the call reports every invalid field again, and the components then show their error.
Help text and feedback ids
text and the feedback get ids of their own. A form control used to hand its
aria-describedby to the text and to the feedback as their id, so the help text and the error
shared one id: the control read the help text and never the error, a valid control with
feedbackInvalid was described by the hidden error, and an aria-describedby that pointed at an
element of the page gave that id to a second element. Now text, feedback, feedbackInvalid and
feedbackValid get generated ids, and the control lists them in its own aria-describedby after
the one you pass: the text always, feedback while validationState is set, feedbackInvalid
only while the control is invalid and feedbackValid only while it is valid, whether that state
comes from validationState or, in a data-coreui-validate form, from the browser’s :user-invalid
and :user-valid (the date and time fields and the pickers use the field’s own verdict instead). A
validationState you set takes precedence. Pass aria-describedby only
to point at elements of your own; a value you passed so that the text would carry it is no longer
needed, and CSS or scripts that found the text by that id need another hook. The pickers drop the
undocumented describedby prop for the same reason, and CFormControlWrapper and
CFormControlValidation replace it with descriptionId, the prefix the ids are built from
(<descriptionId>-label, -text, -feedback, -feedback-invalid and -feedback-valid).
CAccordion
-
Breaking The accordion is built on the native
<details>and<summary>elements.CAccordionItemrenders a<details class="accordion-item">andCAccordionHeaderits<summary>, so the browser owns opening, closing, keyboard interaction and the disclosure semantics — none of it depends on the component being mounted. -
Breaking
CAccordionButtonis removed, together with.accordion-button,.accordion-collapseand.collapsed. The trigger is the<summary>itself and the panel is the element’s own content, revealed through::details-content.CAccordionHeaderalready rendered the button for you, so markup written against the components needs no change; custom CSS keyed on those classes does. Style the open state with.accordion-item[open] > .accordion-header. -
Breaking
CAccordionHeaderrenders a<summary>, not an<h2>. A<summary>is not a heading — where the accordion divides a page into sections, wrap the label in an<h2>–<h6>inside the header. The heading inherits the summary’s typography, so nothing looks different. -
Grouping is the
nameattribute. WithoutalwaysOpenthe items of one accordion share a generatedname, and the browser keeps a single one open. -
Breaking The open items follow the controlled / uncontrolled split the rest of the library uses.
activeItemKeyfed auseStateinitial value, so it read like a controlled prop and behaved like a default: changing it after mount did nothing, and nothing reported back when the user opened an item. It is deprecated in favour of three props that say which is which:defaultOpenItemKeys— the uncontrolled initial value, whereactiveItemKeymaps.openItemKeys— controlled. The accordion renders exactly what you pass, so it has to be fed back fromonChange.onChange— fired with the new list in both modes.
They are named for what an accordion holds.
activeis the vocabulary of a selection — one item out of a set, which is whatCTabshas — while a<details>element is open or closed, and that is whatCAccordionItemcalls its own prop.All three take a single key as well as an array; an array only opens more than one item under
alwaysOpen, since the HTML Standard forbids leaving more than one open item in anamegroup. -
New:
onShown/onHiddenonCAccordionItem, fired from the element’s owntoggleevent — so they report the state whichever path opened the item, including the browser closing a sibling of the samename. -
The panel animation is CSS-driven through
interpolate-size: allow-keywords, which only Chromium implements today. Elsewhere the component measures the two sizes and the stylesheet times the move, exactly asCCollapsedoes; that part goes away, without a breaking change, once Firefox and Safari shipcalc-size(). -
The indicator can be an element. Put a node carrying
accordion-iconin the header and it replaces the chevron the header draws for itself; a second one markedaccordion-icon-activeis swapped in while the item is open. -
New:
themeonCAccordiontints the open item. Closed items are untouched, so it marks what is expanded rather than recoloring the control. -
Breaking
CAccordionItemtakesopen, andvisibleis deprecated. The item renders a<details>element and that is what the attribute is called there, so the prop no longer carries a second word for the same state. It is also the controlled form for a single item, for the casesopenItemKeysdoes not cover;visiblekeeps working. -
A collapsed item stays findable.
<details>keeps its content searchable and the browser expands the item on a find-in-page match, so the accordion needs no counterpart toCCollapse’shiddenUntilFound.
CAlert
dismissibleis no longer required to position a close button. An alert makes room for a<CCloseButton />rendered as its direct child and positions it on its own. The prop keeps working and stays the way to get the built-in button. The button has to be a direct child to be pushed to the trailing edge — one nested in a wrapper is laid out by that wrapper.- Breaking
colorapplies.theme-{color}, not.alert-{color}. The two emit a byte-identical block of--cui-theme-*declarations — the.alert-*names survive in v6 only as back-compat aliases, and every other themeable component reads the same class. Markup that still carries.alert-successrenders exactly as it did; the component just stops generating the older spelling. - Breaking
variant="solid"renders.alert-solidalongside the theme class, notbg-{color} text-white. The old pair hard-coded white text, which fails WCAG AA on five of the eight theme colors — 1.10 onlight, where white on near-white is invisible..alert-solidreads the theme’s own contrast token, so every color lands above the threshold, including one you define yourself. The value also narrows from'solid' | stringto'solid'; v6 has no other alert variant. - Breaking The alert lays its children out in a row.
It is
display: flexwith--cui-alert-gapbetween children, so an icon beside a label needs nod-flexand no spacing utility. Text mixed with inline elements is unaffected, but an alert built from several block elements — a heading, paragraphs, an<hr>— now sits side by side. Wrap that content in a.vstack. - An alert rendered with
transitionfades in when it is added to the page, not only out when it is dismissed. Nothing to change;@starting-stylesupplies the state the animation was missing in that direction. - An
<hr>inside an alert takes the alert’s border color rather than the page’s neutral grey: the theme’s border color, or--cui-alert-border-colorwithout a theme, so a separator follows a border you recolored. Withvariant="solid"the border has the surface color, so the separator takes the theme’s border color instead. - Breaking The alert is rounded with
--cui-radius-7(.75rem), like the dropdown, popover, toast, dialog, drawer and accordion; v5 used.375rem. Set--cui-alert-border-radius: .375remto keep the v5 corners. - Breaking
--cui-alert-borderis gone. The border is written in the rule from--cui-alert-border-widthand the border color, so that a theme can color it. Set those two instead, andborder-styleon the alert for a dashed or dotted line. - Breaking
.alert-dismissibleis no longer rendered. The class carries no styling in v6 — the close button is a flex item pushed to the end withmargin-inline-start: auto, so the alert reserves no extra padding and needs no marker. Thedismissibleprop is unchanged and still renders the button and the self-dismiss behaviour; only the dead class is gone. CSS of your own that targeted it should target.alertor the button instead.
CAutocomplete, CMultiSelect
-
loadingshows a row below the options instead of covering them. The white cover with a spinner that sat over the options whileloadingwas set is gone: the list is markedaria-busy="true", a row with a spinner and the newloadingLabeltext ('Loading…') shows below the options and is announced to screen readers when the shown list starts loading or the list shows up loading, and the options it shows can still be picked with the pointer and the keyboard. -
Breaking The list inside the panel is a
CListBox, so it carries thelist-box-*classes:list-box,list-box-options,list-box-option(+-label,-description,-indicator),list-box-section(+-label),list-box-empty,list-box-header,list-box-select-all, with theactive,selectedandindeterminatestate classes. The panel itself keepscombobox-popup. Thecombobox-option*,combobox-optgroup*,combobox-options*,combobox-headerandcombobox-allclasses are gone, and so are their--cui-combobox-option-*,-optgroup-*and-all-*properties — theme the list through--cui-list-box-*. The formerautocomplete-dropdown/autocomplete-option(s)andform-multi-select-dropdown*/-options*/-option*/-all*/form-multi-selectedclasses are gone as well. This markup is generated by the components, so only custom CSS targeting the panel internals needs updating. -
Breaking
selectAllStyleandoptionsGroupsStyleare removed fromCMultiSelect. One marker serves the whole list:indicatordecides what the options, the group labels and the select-all button all draw. -
Breaking
role="listbox"is on.list-box-options, not on the panel, and a group is wrapped in.list-box-sectionrather than being a label followed by loose siblings. InCMultiSelectthe panel also holds the search field and the select all button, which the old placement claimed as options. -
onShowandonHidefire once per transition, and neither fires on mount.CMultiSelectused to fireonHideevery time the menu opened. -
Breaking
CAutocompletekeeps the selection and the typed text apart.valueanddefaultValuetake an option value, compared as text, so1and'1'select the same option; a string used to be matched against the labels.value={null}selects nothing.onChangereports an option ornull, never typed text: text the user types without picking an option stays in the field and is posted undername. The text is a state of its own:defaultInputValuestarts it,inputValuecontrols it, and the newonInputChangereports every change, picks included, whileonInputreports the search the options are filtered by. Enter on text that names no option no longer reports it throughonChange. FromonChangestoreoption.value,option.labelfor an option without a value, or the string of a plain string option. -
CAutocompleteshows the cleaner for typed text whenallowOnlyDefinedOptionsis off, keeps it out of the Tab order, and leaves the focus in the field after a click. -
Breaking
CAutocompletekeeps the focus in its input. The arrows no longer walk the focus into the panel: the input keeps it and reports the highlighted option througharia-activedescendant, which the option marks with theactiveclass. The input also keeps the keys that belong to its text — Home, End and Space move the caret rather than the highlight. -
Breaking
CMultiSelectopens a dialog instead. The panel isrole="dialog"named by the control, and the focus moves into it: onto the search box whensearchis on, otherwise onto the option the list points at. Options take a rovingtabindexand the arrows move the focus itself, so there is noaria-activedescendantto read any more. Tab cycles through the panel and the search box without leaving them, and Escape closes the panel and hands the focus back to the control. The select-all button is a control over the list rather than a value in it, so it is reached with Tab; selectable group labels are in the arrow order in its place. -
Breaking
CMultiSelect’ssearchno longer takes'global'. Typing reaches the search box from anywhere in the component by default, which is what that value used to turn on, sosearch="global"becomessearchandsearch={{ external: true, global: true }}becomessearch="external". SetglobalSearch={false}to keep typing local: the search box then filters only while it has the focus, and a keystroke over the options list jumps to the first option starting with it (withsearch="external"it does nothing, since the list holds only the last results). -
Breaking
CMultiSelect’ssearchno longer takes an object. It takestrue,falseor'external', sosearch={{ external: true }}becomessearch="external". -
Breaking
CAutocomplete’ssearchtakes only'external', and global typing isglobalSearch, on by default.search="global"goes away: typing on the picker button or in the options panel reaches the field without it. The object form goes too, sosearch={{ external: true, global: true }}becomessearch="external". SetglobalSearch={false}to keep a keystroke where the focus is. The space bar and shortcuts with Ctrl or Cmd stay with the button that has the focus, and a character typed with AltGr or Option reaches the field like any other, inCMultiSelecttoo. -
CAutocompleteandCMultiSelectread the typed text without the spaces around it, in the filter, the highlight, the hint, Enter and leaving the field, andCMultiSelectcreates an option from the trimmed text. A query of spaces lists every option. InCMultiSelectthe search matches option labels only: a group whose own label matches no longer keeps all of its options, and a group without options is not listed. -
CMultiSelectblocks the submit whilevalidationStateis'invalid', and asks to drop the state withonValidationStateChange(undefined)when the user changes the selection or the form is reset. A native form reset now restores the selection given bydefaultValueor the options’selected. See Validation. -
CAutocompleteblocks the submit whilevalidationState(or the deprecatedinvalid) is'invalid', and asks to drop the state withonValidationStateChange(undefined)when the user changes the text or the form is reset. The frame also shows the state a validation reports for the input, where it used to show only the state you gave. See Validation. -
Keyboard,
CMultiSelect: arrow keys skip disabled options. -
Keyboard,
CAutocomplete:ArrowDownout of the input highlights the first enabled option, andEscapehands the focus back to the input. A closed field rendersaria-expanded="false"instead of omitting the attribute. -
Breaking
CMultiSelect’s search field no longer reaches the server. The input that filters the options carried anamebuilt from the field’s own, and a plain text input is serialized, so every form holding aCMultiSelectsubmitted whatever the user had typed to narrow the list. It carries nonamenow and thesearch…key is gone from the payload. -
Breaking An unnamed
CMultiSelectstays out of the form. The native select tookname={name ?? uniqueId}, so a component the page had not named still submitted its value under React’s generated id, a key like:r1:that nobody chose. The attribute is written only whennameis given.CMultiSelectNativeSelectcarried a second fallback of${id}-multi-selectfor the same purpose; it was unreachable throughCMultiSelectand is gone too. -
Breaking An unnamed
CAutocompletestays out of the form too. Its input took a generatednamewhen the prop was not set, so a form carried a key nobody chose. It does not now. The generatedidis unaffected. -
Breaking
CAutocomplete’s typeahead hint no longer takes the field’sid. WithshowHints, the decorative hint input rendered the sameidas the real one, sodocument.getElementById, alabel[for]and the floating label association all resolved to the hint. It carries noidnow. -
Deprecated CMultiSelect:
selectionType="tags"is"chips", andariaTagDeleteLabelisariaChipRemoveLabel. The selection renders chips, so the mode carries their name and"chips"is the default."tags"renders the same until v7. The old label prop still names the remove button and takes precedence while it is set. -
Breaking CMultiSelect’s classes drop the
form-prefix, so they carry the component’s name like.autocomplete,.chip-inputand.date-picker. There is no alias; update CSS that targets them.v5 v6 .form-multi-select.multi-select.form-multi-select-selection,.form-multi-select-selection-tags.multi-select-selection,.multi-select-selection-chips.form-multi-select-search,.form-multi-select-placeholder.multi-select-search,.multi-select-placeholder--cui-form-multi-select-selection-tags-gap,-padding-y--cui-multi-select-selection-chips-gap,-padding-yThe filled state is
.multi-select-filled, and the component stylesheet is@coreui/coreui-pro/css/components/forms/multi-select.css. -
CMultiSelect: Backspace in the empty search box focuses the last chip. It removed the last value at once; the first press now moves the focus to the last chip and the second removes it, as in
CChipInput. The arrow keys move between the search box and the chips, the selection is one tab stop, removing a focused chip moves the focus to its neighbour, and a value removed through a chip, Backspace or the cleaner is announced (ariaRemovedAnnouncement). A single select and thecounterandtextselections still remove the last value at once.
CAvatar
- Breaking
textColoris removed. It wrote a singlefg-{value}class, and the values it listed from v5 (body-secondary,body-tertiary,*-emphasis,black-50,white-50) had no rule in v6, so the text kept its inherited color. Put the color utility on the component instead:className="fg-2"forbody-secondary,fg-3forbody-tertiary,fg-emphasis-primaryforprimary-emphasis,text-body-emphasisforbody-emphasis,fg-white fg-50andfg-black fg-50forwhite-50andblack-50, andfg-{color}for a theme color. - Breaking
colorapplies a theme class, not a background utility. It renderedbg-{color}, which left the letters on the inherited color and is why the docs paired it with a hand-pickedtextColor— a recipe that failed WCAG AA on five of the six examples they shipped..theme-*supplies the background and the color that reads on it, so drop thetextColorthat went with it. A bare<CAvatar>is a neutral circle instead of a transparent one. - New:
variant="subtle"— a tinted avatar that keeps the theme color in the letters rather than behind them. - New:
ariaStatusLabelnames the status indicator, renderingrole="img"with the label. The indicator is color and nothing else, so a state that carries meaning needs a name;aria-labelon a bare<span>has no role to attach to and may be ignored. Without the property the indicator stays decorative, as before. sizeis typed onCAvatarandCAvatarStack:xs,sm,md,lg,xl— the five sizes the stylesheet ships — instead of any string.- The Sass variables behind the size moved:
--cui-avatar-width/-heightbecome--cui-avatar-size, and--cui-avatar-status-width/-heightbecome--cui-avatar-status-size. Only inline overrides of those names need updating.
CBadge
- Breaking
textColoris removed. It wrote a singlefg-{value}class, and the values it listed from v5 (body-secondary,body-tertiary,*-emphasis,black-50,white-50) had no rule in v6, so the text kept its inherited color. Put the color utility on the component instead:className="fg-2"forbody-secondary,fg-3forbody-tertiary,fg-emphasis-primaryforprimary-emphasis,text-body-emphasisforbody-emphasis,fg-white fg-50andfg-black fg-50forwhite-50andblack-50, andfg-{color}for a theme color. - Breaking
colorapplies a theme class, not a background utility. It renderedbg-{color}, which in v6 leaves the badge on the inherited text color instead of white — the.badge-*namespace carries the variants now, and the color comes from.theme-*. The prop is unchanged at the call site; only the class it emits moved. A bare<CBadge>is neutral rather than invisible, so a badge that relied on a utility for its background can drop it. - Breaking
colorno longer takes a gradient. v6 removes thebg-*-gradientutilities the value mapped to. - Breaking
textBgColoris gone. It emitted.text-bg-{color}, which resolves to the same two valuescolornow supplies through the theme class —--cui-{color}-contraston the text and the colour itself behind it — so the two props had become one. Keeping it was also destructive:.text-bg-*ships in thehelperslayer and writescolorandbackground-colordirectly, over the tokens.badgereads, so pairing it withvariant="outline"filled in the background the variant had cleared. Usecolor. - New:
variant—"subtle"or"outline", alongside the default solid. sizealso takes"lg".
CBreadcrumb
- Breaking A step is a padded, hoverable target.
CBreadcrumbItemrenders.breadcrumb-linkon the link itshrefproduces, and wraps an active step in a<span className="breadcrumb-link">so every step keeps the same height. To keep the plain text link of v5, render your own link as a child instead of passinghref. - New:
CBreadcrumbDivider— a separator you put in the markup, carrying whatever you give it: an inline SVG, an icon, a character. An empty one renders the default divider. The automatic::beforedivider steps aside wherever an element is placed, so the two never double up, and the component marks itselfaria-hidden. - A step can open a menu. Wrap the step’s content in a
CMenuwhoseCMenuToggletakes the newcustomprop, and the toggle behaviour is wired onto your own.breadcrumb-linkbutton instead of aCButton. See Collapsed items.
CCalendar
- Changed. The range band sits under the day cells and follows the theme.
--cui-calendar-cell-range-bgis--cui-primary-bg-subtle(the theme’sbg-subtleunder a.theme-*class) instead of a 12.5% tint laid over the cells, so the selected start and end keep their solid fill and the day numbers are no longer tinted. A hovered day in the range takes--cui-primary-bg-muted(the theme’sbg-muted), one step stronger than the band. - Changed.
minDateandmaxDateare compared by the day. v5 compared a day cell at midnight with the instant, so a limit that carried a time of day, such asminDate={new Date()}, disabled its own day. Now that day can be picked; the date picker’s time selects and the field bound the time on it. - Changed. A week row stands for the ISO week that holds most of its days. With
firstDayOfWeekother than1a row spans two ISO weeks, and v5 numbered it by its last day but picked the week of its first day: a Sunday-first row from August 30 to September 5, 2026 showed 36 and picked2026W35, and that value given back asstartDatedid not select the same row. The number, the picked value, the weeks ofstartDate,endDate,minDateandmaxDate, and the range and its preview now all follow the row’s majority week, so that row is2026W36and the picked date is its Monday, August 31, the date2026W36stands for. With Tuesday to Thursday first, the numbers move instead, so the row from September 1 to 7 shows 36 where it showed 37. - Changed. In week selection
disabledDatesdisables a week only when it disables every day of it, as it does for a month, quarter or year. Before, a week was disabled when its row’s first day was: with Monday first a single disabled Monday disabled its week, and with Sunday first a function that disables weekends disabled every row. A range of weeks across a week that holds a disabled day is kept, as a range of months is. - Changed. In week selection
minDateandmaxDatecount as whole weeks, so their weeks can be picked on any first day of the week, as the month, quarter or year of a limit can in those selections, and the months view opens a month only when its page holds a week inside them. Before, aminDatelater than the first day of its row disabled that row, and with Tuesday to Thursday first amaxDateof2026W36disabled the row of week 36. - Breaking
onStartDateChangeandonEndDateChangereceive theDatefirst, the same shape the pickers use, and the value formatted perselectionTypesecond — aDatefor day selection but a string for week ('2026W27'), month ('2026-01'), quarter ('2026Q1') and year ('2026'). v5 passed them the other way round. - Breaking
CCalendarno longer reports a date that stays empty. Picking a day before the start of a range, or an end across a disabled day, used to callonEndDateChange(null)although no end had been picked, and clearing a range across a disabled day before a start was picked calledonStartDateChange(null). A day picked again is still reported, as in the Vue library. - Breaking
selectAdjacementDaysandshowAdjacementDaysare spelledselectAdjacentDaysandshowAdjacentDaysonCCalendarand every picker built on it. The typo is fixed in the vanilla and Vue libraries in the same release. - Breaking
onDateHoveris gone fromCCalendar,CDatePickerandCDateRangePicker, ascellHoveris from the vanilla calendar. It served the hover preview in the field, which went withpreviewDateOnHover. To follow the pointer or the focus over aCCalendar, listen formouseoverorfocusinon an element that wraps it: each day, month, quarter and year cell the calendar shows carries its date indata-coreui-date, in thetoDateString()form, and a week row carries it on the day cells it shows. Unlike the event, the listeners also see cells that cannot be picked, and onlymouseoutorfocusouttells that the pointer or the focus left. The pickers have no replacement for it. - Breaking Each grid is one tab stop, and
tabindexno longer marks a selectable cell. The arrows,Home,Endand the page keys move within a calendar and Tab leaves it, which is whatrole="grid"promises. Every cell insideminDate/maxDatecan take the focus, also one that cannot be picked: the keys stop on a date indisabledDatesinstead of skipping it, a screen reader announces it as unavailable, and Enter and Space do nothing there, so a month where nothing can be picked is explored like any other. A selectable day, month, quarter, year or week row carriesdata-coreui-selectable, andtabindex="0"sits on one cell per grid, so a two-month range picker keeps a stop in each month that reaches into the limits. Custom CSS or scripts that reached for.calendar-cell[tabindex="0"]need.calendar-cell[data-coreui-selectable]. - Breaking
navigationandnavYearFirstare gone fromCCalendar,CDatePickerandCDateRangePicker. Withnavigation={false}the month and year buttons stayed on screen but did nothing; the arrows are always there now. To hide them, hide.calendar-nav-prevand.calendar-nav-nextin your CSS. The month and year buttons still open the months and years views; to keep the calendar on the month you set, hide.calendar-navand give the month a heading of your own. The order of the year and the month follows the locale instead ofnavYearFirst: the year comes first in locales that write it first (for example Japanese, Chinese, Korean, Hungarian or Lithuanian). - Breaking The calendar’s classes and data attributes are the
vanilla calendar’s. A panel is
.calendarwithout the view class (days,months,quarters,years) and carries its position indata-coreui-calendar-index; the view is named on the cell content instead (.calendar-cell-inner.day,.month,.quarter,.year), and quarter and year cells no longer carryquarters/year. The navigation buttons carrybtn-double-prev,btn-prev,btn-month,btn-year,btn-nextandbtn-double-next, anddata-coreui-selectableis empty instead of"true". A rule that styled a panel by its view needs.calendar:has(.calendar-cell-inner.month)instead of.calendar.months, and one that styled.calendar-cell.quartersor.calendar-cell.yearneeds.calendar-cell:has(> .calendar-cell-inner.quarter)or.year, since the cell states (selected,range,disabled) stay on the cell. - Breaking Setting
calendarDatetonullkeeps the month on screen. v5 moved the calendar back to today; to show today, pass today’s date. An emptycalendarDate(nullor'') at mount no longer opens the calendar on today: it opens onstartDateorendDate, as whencalendarDateis left out; v5 opened on today fornull. onViewChangedis deprecated in favour ofonViewChange, the tense every other callback uses; both fire while the old name is set.- The grid answers the full APG keyboard model. On top of the arrows:
Home/Endmove within the focused week,Page Down/Page Upmove a month at a time (a year in the months and quarters views, the twelve years of a page in years), andShiftwith either moves a year in the days view. A jump never lands outsideminDate/maxDate: focus stops on the limit, also when that date cannot be picked. Every cell carries its owndata-coreui-date. selectionType="week"is reachable from the keyboard. The day cells take no focus in that mode because the row is what gets picked, and the row itself was not a tab stop — so the grid had none at all and week selection was mouse-only. The rows are tab stops now, andHome/Endmove to the first and last week of the month.- Picking a date keeps the months on screen. A picked day of an adjacent month
(
selectAdjacentDays) used to turn the calendar to that month, and so did any pick the parent passed back throughstartDate: a start date picked in the second month ofCDateRangePickershifted both months by one. A picked day of an adjacent month now shows as selected with an outline on a subtle background instead of the solid fill. The view still followscalendarDate, and without it a start date the parent changes to a new instant, the day already selected included (a Today button after browsing); it stays for the calendar’s own picks passed back before the view moves and for the same instant passed again. An end date moves it while no start date is set, unless the calendar already holds that end. - A selected day or week keeps its colour under the pointer, a shade darker
(
--cui-calendar-cell-selected-hover-bg,--cui-calendar-cell-selected-hover-color); it used to take the hover colour until the pointer left. - The range preview no longer stays on the date just picked, and in week selection it no longer drops while the pointer crosses a day of the adjacent month inside the row it previews.
- In the years view the navigation names the page it shows,
2020 – 2031, instead of the one year the page is built around, so turning a page is announced as the new range. The grid label and the navigation write the range in the locale’s own digits. - In the years view the double arrows and
Page Down/Page Upturn the calendar by the twelve years a page shows instead of ten, so a turn no longer repeats two years of the previous page. - In the years view the year button goes back to the view that picks the date instead of doing nothing, and it is disabled in year selection, where the years view is the one that picks.
- Without
weekNumbersLabelthe week-number column is named for screen readers with the locale’s word for week, in a visually hidden span; before, its header was empty. Each week number is named the same way with its number (Week 36), and the newariaWeekNumberLabelprop takes a function of the week number to name it yourself. - Breaking The years view names its double arrows with new
ariaNavNextYearsLabel/ariaNavPrevYearsLabelprops ('Next 12 years'/'Previous 12 years') instead of the year labels, which stay on the days, months and quarters views. A page that translatesariaNavNextYearLabel/ariaNavPrevYearLabelneeds to translate these two as well. - A grid in which nothing can be picked is described by the new
ariaNothingToPickLabelprop ('Nothing on this page can be picked'), also announced when the calendar turns to such a page, so a page that translates the other calendar labels translates this one too. - A pick is announced through the new
ariaSelectedDateAnnouncementandariaSelectedRangeAnnouncementprops ('Selected date: {date}','Selected range: {start} to {end}'); before, picking a date with Enter left a screen reader silent. A page that translates the other calendar labels translates these two as well. - The toggle of
CDatePickerand ofCDateRangePickeris named with the value the picker holds (Toggle calendar, Friday, August 14, 2026), so a screen reader reads the picked date when the popup closes and focus goes back to it. - The month and year between the navigation arrows are no longer a live region. A page turn is
announced through
announce(), inside the panel of a date picker when the calendar sits in one, and a key that leaves a day or a week row names no page, since the date that takes focus names it: a key that moved from August 31 to September 1 used to be read twice, as the date and then asSeptember 2026. - A locale whose own calendar has different months (Persian by default in
fa-IR, or an Islamic, Hebrew or Chinese calendar asked for with-u-ca-) now shows the calendar in the Gregorian calendar, in its own language and digits. The grid was always Gregorian, sofa-IRused to show September 1, 2026 as the 10th of Shahrivar 1405 and name the January cell Dey. Calendars that share the Gregorian months, such as Thai Buddhist or Japanese, keep their year numbering in the calendar. The date field lays out and reads Gregorian numbers for every locale, without the locale’s era. - Breaking A cell drawn by
renderDayCellno longer gets anaria-label, so its content is what a screen reader reads. Every render prop receives the cell’s full name asmeta.label(Wednesday, August 12, 2026,August 2026,Q3 2026,2026); put it in the cell, for example in avisually-hiddenspan next to a short visible label markedaria-hidden="true". Day cells the calendar draws itself announce the full date with the weekday instead of the short numeric date (8/12/2026), which also leaves theirtitletooltip, and in week selection the row that takes focus is named by the days it spans (August 10 – 16, 2026), after its week number when week numbers are shown (Week 33, August 10 – 16, 2026), instead of reading out all seven. renderDayCellreceivesisToday: falsefor the copy of today among an adjacent month’s days, which takes neither thetodayclass noraria-current="date"either, and in day selectionisInRange: falsefor a day among an adjacent month’s days that only fills the grid, which the range is not drawn on. WithselectAdjacentDaysthe range and its hover preview are now drawn on the adjacent month’s days as well, andisInRangestaystruefor them.- A cell that cannot be picked carries
aria-disabled="true": a day outsideminDate/maxDateor indisabledDates, a week row, month, quarter or year outside the range, and in day selection a day among an adjacent month’s days unlessselectAdjacentDaysis on. Such an adjacent day is no longeraria-selectedeither, andrenderDayCellgetsisDisabled: trueandisSelected: falsefor it, so the copy of a picked date that fills the next panel reads as unavailable filler. - With
range, the grid of the view that picks the range isaria-multiselectable, and every day, week row, month, quarter or year of the selected range that can be picked carriesaria-selected="true", not only its two ends, so a screen reader announces the ones in between as selected. The months and years views of a day range only navigate and keep it on the ends, andisSelectedstill marks only the ends. - In week selection,
renderDayCellgets theisDisabled,isInRangeandisSelectedof the day’s week row, the way the calendar draws it: every day of a selected week isisSelected, every day of a week in the range isisInRange, and a day isisDisabledonly when its row cannot be picked. Before, each field described the day alone, so a selected week reported only the day holding its start or end date as selected. - A year below 100 stays as written: a calendar on December of year 99 draws the days of year 99,
where it drew 1999, and its next panel shows January of year 100, where it showed January 2000.
The month, quarter and year views, the arrow keys and paging keep the year the same way. Years
before 1 cannot be picked, as dates before
minDatecannot: their cells are disabled, and the arrow keys and paging stop at 1 January of year 1. - A year string written in four digits keeps its year:
0026-05,0026W05,0026Q2,0026and12/31/0099given to the calendar or a picker stand for the years 26 and 99, where they became 2026 and 1999. Two digits still expand to the century within 50 years of today. The value reported for week, month, quarter and year selection writes the year in at least four digits (0026-05, where it was26-05), so it reads back as the same year.
CCallout
- New:
theme. It sets the color context and renders a.theme-*class, so the callout also picks up a theme class placed on any element above it. The class colors the leading edge and tints the border. - Deprecated
colorstill works and is removed in v7. It renders the same.theme-{color}class asthemeand loses tothemewhen both are set, so the modifier classes the component used to emit (.callout-{color}) are gone from its output. - A callout without a color no longer draws its leading edge in
currentColor. The class read a custom property that only the color modifiers declared, so on a bare<CCallout>the declaration was invalid and the property fell back to its initial value. It now defaults to the callout’s own border color. - The 1px border of a colored callout is tinted instead of staying neutral. The leading edge is unchanged.
Deprecated color classes
.alert-{color},.btn-{color},.btn-outline-{color},.btn-subtle-{color},.btn-ghost-{color},.callout-{color},.chip-{color}and.list-group-item-{color}ship incoreui-legacy.css, not in the component stylesheets. The components render theme classes now, so none of them emit those spellings — the sheet only serves markup you wrote by hand. Import it while you still need it, and drop it once your markup uses the theme classes. They are removed outright in v7.- The
colorproperty renders the theme classes.<CButton color="primary" />rendersbtn btn-solid theme-primaryrather thanbtn btn-primary, so an app that has not moved offcolorkeeps its styling without loading the legacy sheet. CFormCheckandCFormSwitchimportcoreui-legacy.cssthemselves. They render the v5.form-checkmarkup by design; the sheet comes with them, so nothing changes for you.
CCard
- Breaking
textColoris removed. It wrote a singlefg-{value}class, and the values it listed from v5 (body-secondary,body-tertiary,*-emphasis,black-50,white-50) had no rule in v6, so the text kept its inherited color. Put the color utility on the component instead:className="fg-2"forbody-secondary,fg-3forbody-tertiary,fg-emphasis-primaryforprimary-emphasis,text-body-emphasisforbody-emphasis,fg-white fg-50andfg-black fg-50forwhite-50andblack-50, andfg-{color}for a theme color. - Breaking A card is roomier, rounder and framed in the solid
border colour.
--cui-card-spacer-y/-xgo from1remto--cui-spacer-6(1.25rem) and the image-overlay padding follows them.CCardHeader,CCardFooterand aCListGroupplaced directly in the card take--cui-card-spacer-xas their inline padding, so their text lines up with the body; the header and footer keep.5remof block padding. The border reads--cui-border-colorinstead of--cui-border-color-translucent, except on atranslucentcard, and the radius is--cui-radius-7(.75rem; v5 used.375rem). A card filled withtextBgColororcolorkeeps that neutral frame; add the matchingborder-<color>class to frame it in the fill colour. To keep the v5 look, set--cui-card-spacer-yand-xtovar(--cui-spacer-5)and--cui-card-border-radiusto.375remon the card. - Breaking
CCardLinkis removed..card-linkis gone from v6 — it existed to space sibling links with an inline margin, which only worked while they were inline, and.card-bodyis a flex column with--cui-card-body-gapnow. Wrap the links yourself:<div className="hstack gap-4"><CLink …/><CLink …/></div>. - Breaking
.card-bodyis a flex column. It spaces its children with--cui-card-body-gapand zeroes their block margins, so nothing in a card needs.mb-*any more. Two consequences for existing markup: a direct child takes its natural width rather than filling the card — add.w-100where a block child has to stretch — andtext-alignno longer moves a button or a link, because they are flex items. Use.mx-autoor.ms-autoinstead. - Stacked sections are divided by a single rule. A
<CCardBody>, a list group or a bottom image that follows a top image, a body or a list group draws a divider above itself in the card’s own border, so two bodies in a row, or an image above a body, no longer sit with nothing between them, and a border utility or a theme on the card carries over to every divider. A section hidden withhiddenor.d-nonedoes not count.<CCardHeader>and<CCardFooter>draw their own divider, as before. <CCardGroup>squares the corners at each seam along the writing direction. The rules named the physical corners, so a right-to-left group squared its outer corners and rounded the ones that meet. A list group at the edge of a grouped card is squared like the other sections.- Breaking
colorno longer takes a gradient. v6 removes thebg-*-gradientutilities the value mapped to, so theGradientsunion is gone from the prop’s type. Reach for.bg-gradientalongside the color if you want the sheen. styleonCProgresslands on the track. It used to travel to the bar with the other attributes; now it stays on the.progress, as it does in the Vue edition, so the segment and gradient variables set there reach the mask and the bar alike. The other HTML attributes still go to the bar.- Gradient bars.
--cui-progress-bar-bg-imagepaints the bar with an image over its colour, so a gradient is one variable instyle; stripes layer over it and have their own--cui-progress-bar-stripesand--cui-progress-bar-stripe-color. segmentedandsegmentsonCProgress. The track becomes a row of rounded segments —--cui-progress-segment-min-widthwide and--cui-progress-segment-gapapart, or at mostsegmentsof them — and the bar fills whole segments only, recounted as the track resizes. Corners come from--cui-progress-segment-border-radius.- Breaking
.card-header-pillsis gone. It pulled the pill row out to the header’s padding edge, which is wrong for pills — they carry their own background, so flush against the inner edge they touch the border. Drop the class from<CNav variant="pills">. - Breaking
<CCardGroup>attaches from a container query, not a media query. Thesmbreakpoint is the width of the nearest query container, not the viewport. Mark an ancestor with.contains-inline; without a container the query has nothing to read and the cards stay stacked at every width. The same applies to<CListGroup>withlayout="horizontal-*". - New:
themepaints the card’s frame and caps with a theme color, with contrast text in the header and footer; the body keeps the card background. It is a different axis fromcolor, which still applies thebg-{color}utility across the whole surface, and fromtextBgColor. Put the class on a single section —<CCardFooter className="theme-danger">— and only that section follows it. - New:
variant="subtle"swaps the solid fill for the theme’s subtle background and border, and new:translucentblurs whatever is behind the card and thins the caps to match. They are independent —.card-subtleand.card-translucentcombine — which is whytranslucentis a boolean of its own and not a secondvariantvalue. - New:
layout="row"lays a card out along the inline axis, with<CCardImage orientation="start" | "end">for an image cap on either side. It replaces the<CRow className="g-0">+<CCol md={4}>recipe, which needed a wrapper per section and drew no divider between them.
CChip, CChipSet, CChipInput
-
Breaking
CChipInputcarries its chips in a hidden<textarea>, submitted only once you name it. It took a generatednamewhen the prop was not set, so a form carried a key nobody chose; the field is now named only by the prop. It is a<textarea>instead of an<input type="hidden">, because the browser leaves a hidden input out of validation, and the submitted value is the same, the chips separated by commas. With itCChipInputtakesrequired,validationState,onValidationStateChangeand the feedback props, blocks the submit while it is empty and required, and a native form reset puts back the chips it started with. A disabledCChipInputis no longer submitted, like any disabled field. See Validation. -
Breaking
CChipdropsactive;selectedis the one prop that renders.active. On aselectableorfilterchipselectedkeeps its controlled toggle semantics (witharia-pressed/aria-selectedand the filter check icon); on any other chipselected={true}only renders the.activeclass — no toggle, no ARIA state — which is whatactivedid. Replaceactivewithselected. -
Breaking The chips expose real roles. A selectable
CChipSetis a horizontalrole="listbox"(witharia-multiselectableoutside single mode) whose chips arerole="option"carryingaria-selected; a non-selectable set is arole="group". A standalone selectableCChipis a toggle button (role="button") and reflects its state througharia-pressedinstead ofaria-selected. -
Breaking
aria-disabledis stamped only on a chip that carries a role. On a role-less chip the attribute was invalid; thedisabledclass and the removed interactivity carry the state. -
Breaking
CChipInputno longer stampsaria-disabled/aria-readonlyon its container. The container is a generic element, so those attributes were invalid on it; the nativedisabled/readonlystates of the inner input carry the semantics. -
Adding and removing chips is announced.
CChipSetandCChipInputannounce each change throughannounce(); the wording is configurable throughariaAddedAnnouncementandariaRemovedAnnouncement. -
Every remove button names its chip.
CChipandCChipSetnamed each remove buttonRemove, so a set read the same name for every chip; the button isRemove <text>now, asCChipInputandCMultiSelectalready did.ariaRemoveLabel(andariaChipRemoveLabelonCMultiSelect) takes a string, where{label}stands for the chip’s text and is appended when missing, or a function of the text for a different word order. A label that already spelled the text out, such asariaRemoveLabel={`Remove ${chip}`}, now repeats it; drop it or use{label}. -
A selectable chip hides its remove affordance. A chip with a role — a selectable chip, which is a toggle button, or an option in a selectable set — gets a
<span aria-hidden="true">instead of a<button>inside it, so its name is its text and a control is no longer nested in a button. Backspace and Delete remove it from the keyboard, the affordance from the pointer. -
A chip set is one tab stop.
CChipSetandCChipInputgave every chiptabIndex={0}, so Tab walked through each chip. The chip that last had focus is the tab stop now, the first chip until then, and the arrow keys move between the rest, chips without avalueincluded.CChipcomputes itstabIndexfromselectable,removableanddisabled; atabIndexpassed to it is ignored. -
CChipInputkeeps focus among the chips after a removal. Removing the focused chip sent focus to the text input; it now moves to the next chip, else the previous one, and reaches the text input once no chip is left, the ruleCChipSetfollows.
CCloseButton
-
Breaking The
whiteanddarkprops are gone. The glyph is drawn with a CSS mask and takes the button’s inheritedcolor, so on any surface that paints its own text — a.text-bg-inversetoast, a dark navbar’s offcanvas — it is already the right color, and neither prop has anything left to do.whiterendered.btn-close-white, which v6 removed.darkstampeddata-coreui-theme="dark"on the button, which used to match a rule that inverted the glyph through afilter; that rule went with the mask, and what the attribute still sets — the shadow strength andcolor-scheme— reaches nothing the close button draws. To flip a region, put the attribute on the section and give it a surface (.bg-body.text-body); the button follows the text it inherits.
CCollapse
- The stylesheet owns the animation where the browser can interpolate a size to
auto.CCollapseno longer writes an inlineheight/widththere — driving the size from React as well would run the same expansion from both ends. Browsers withoutinterpolate-sizekeep the measured path, and both read the duration and easing from--cui-transition-collapse-duration/-timinginstead of a hardcoded 350 ms. - Breaking
.collapsestays on the element for the whole run. It used to be dropped while.collapsingwas on, which in v6 means the element missesinterpolate-sizeand the transition the stylesheet puts on.collapse— so the CSS path would not animate at all..showalso lands at the start of the opening run rather than at its end, because the stylesheet reads it as the open state and animates towards it. Custom CSS that keyed off the old combinations needs updating. - New:
hiddenUntilFoundhides the collapsed area withhidden="until-found"rather than taking it out of the layout, so find-in-page searches it and the browser expands it on a match.onShowfires when that happens — setvisiblefrom it, or the stylesheet hides the content the reader was just sent to. Off by default; the attribute is left off entirely below Chrome 102 / Firefox 148 / Safari 26.2.
CConditionalPortal
CConditionalPortalresolves its target while rendering. It used to render its children in place for one commit and then teleport them, which remounted the subtree and dropped any focus those children had taken.
CContextMenu
- New component.
<CContextMenu>is the trigger area: a right-click, or Shift+F10 / the Menu key while it has focus, opens the<CMenuContent>inside it at the pointer and suppresses the browser’s own menu. It is aCMenupositioned against the pointer withposition: fixed, so items, submenus, keyboard navigation andautoCloseare the Menu’s. Focus moves into the menu on open and returns to the area on close; the page does not scroll while the menu is open. A row’s ownonContextMenuhandler runs first — callpreventDefault()there to keep both menus closed.
CDateInput, CDateRangeInput, CTimeInput
- Breaking A required field blocks the form. The value
moved from an
<input type="hidden">, which the browser leaves out of validation, to a<textarea>laid invisibly over the field, withtabIndex={-1}andaria-hidden="true"; a textarea, unlike a text input, leaves Enter submitting a form that has no submit button. Arequiredfield that is empty, incomplete or holds a rejected date now stops the submit, and the browser puts focus on its first empty section with its own message over the field. The field marks itself invalid whenever a validation reports it, also outside a submit, and its value field fires nativeinputandchangeevents when the date changes, whichonInputon a form sees and React’sonChangedoes not. This applies to the pickers and to both ends of a date range as well. Code that selectsinput[type="hidden"]inside the field needs.form-date-time > textarea. - Breaking A given state is a verdict on one value.
validationState="invalid"now blocks the submit, with the text of the message after the field, until it is cleared. The user’s next change of the value and a native form reset callonValidationStateChange(undefined)before the change is reported, so pass your state setter there; a value your code sets keeps the state. Without a given state the field shows what a validation reports and, in a form withvalidate, the:user-invalidand:user-validstates of its value field, as a native field does.aria-invalidsits on the sections, not on the group, and anaria-invalidyou pass is not read as a state. This applies to the pickers and to both ends of a date range as well.
CDatePicker, CDateRangePicker, CTimePicker
The pickers are no longer monoliths. Each is a composition of a section field, a calendar or time selection, and an anchored panel — which is what removes most of the props below: the field and the panel are now the same primitives you can use directly.
-
Breaking
CDatePickeris its own component. In v5 it was a thin wrapper overCDateRangePickerand inherited its props; it now has its own, single-date API (date,defaultDate,onDateChange). -
Breaking The button-configuration props are gone —
todayButton,todayButtonColor,todayButtonSize,todayButtonVariant,cancelButton*,confirmButton*andrangesButtons*. Project the content instead: thefooterprop takes a node, or a function receiving the picker’s context, so a button you render yourself can act on the picker and read its state. The same context is available to any descendant throughuseDatePicker(),useDateRangePicker()anduseTimePicker().<CDatePicker footer={({ today, clear, close, isDateSelectable }) => ( <> <CButton disabled={!isDateSelectable(new Date())} onClick={() => isDateSelectable(new Date()) && today()} size="sm">Today</CButton> <CButton onClick={clear} size="sm">Clear</CButton> <CButton onClick={close} size="sm">OK</CButton> </> )} /> -
Breaking
CDateRangePicker:rangesis a projected region too. It takes a node or a function receiving the context, so a preset button callssetRangewith its own dates;rangesButtonsColor/-Size/-Variantare gone with it. The controlled range isstartDate/endDate(withdefaultStartDate/defaultEndDatefor the uncontrolled mode), and the two fields takestartName/endName. -
A picker action shows its date in the calendar.
setRange,reset,setDateandtodayfrom the picker’s context turn the calendar to the date they set (a range’s start, else its end) in the view it is in, also when the value does not change and over acalendarDateyou pass, so a preset clicked again after browsing brings its range back.clearand an invalid date leave the calendar where it is; a typed date, a picked time and the dates you pass move it as before. -
Breaking A date field is submitted only once you name it.
CDatePickerandCDateRangePickerbuilt the name of their inputs fromidwhennamewas absent, so<CDatePicker id="delivery" />posteddelivery-dateand the range posteddelivery-start-date/delivery-end-date. That fallback was deprecated in 5.3.0 and is gone: anidnames the element for a label, it does not put the value in the payload. Passname, orstartName/endName, for the field to reach the server. -
Breaking The field props are gone:
inputDateFormat,inputOnChangeDelay,inputReadOnly,placeholder,separator. The value is a mask now — passformat(or a section layout) and, for the placeholder text, one prop per section —dayPlaceholder,monthPlaceholder,yearPlaceholderand the rest. The range placeholder also named the two fields;CDateRangePickernames them withariaStartLabelandariaEndLabelnow (“Start date” and “End date” by default), or withfloatingLabelswhen set. UsereadOnlyfor a read-only control andseparatorIconfor the artwork between the two range fields. -
Kept.
timepickerstill adds the time half toCDatePicker, now as a row of time selects under the calendar rather than a second panel beside it. Because the two halves own different parts of one value, a picker with time does not auto-close: picking a day keeps the time and picking a time keeps the day.hours,minutesandsecondsnarrow what the time half offers. -
Changed. With
timepicker, aminDateormaxDatethat carries a time of day bounds the time on its day: the time selects disable the hours, minutes and seconds beforeminDateor aftermaxDatethere, a time the panel sets beyond the limit (the current time when that day is picked) moves to the limit, and a typed time beyond it marks the field invalid. A limit without a time of day covers its whole day. -
Breaking
hoursis 0 to 23 in every locale. v5 read the list and the filter in the column’s cycle, so on a locale whose v5 columns were 12-hour (the ones that write AM and PM in Latin letters, such asen-US)hours={[1, 3, 5, 7]}meant 1, 3, 5 and 7 in the morning and in the afternoon,12meant noon and midnight, and a filter received 1 to 12. The 12-hour column still lists 1 to 12 next to AM/PM, but each entry now stands for the 24-hour hours the list allows: picking an hour that only one period allows sets that period, and the period the picked hour does not allow is disabled. To keep such a list, name both halves:hours={[1, 3, 5, 7, 13, 15, 17, 19]}, with0and12for12. More locales get 12-hour columns than in v5, because the columns follow the field’s clock, Korean, Traditional Chinese, Arabic and Mexican Spanish among them; their v5 columns were 24-hour, so a list written for them keeps its meaning. The day period column also speaks the locale, 오전 and 오후 in Korean, where it read “AM” and “PM”. A longer name widens its column, so--cui-time-picker-cell-widthis now the narrowest a cell gets, and--cui-time-picker-cell-padding-xkeeps the name off the cell’s edges. -
Breaking The time picker’s roll classes lose the
rollinfix, and the date picker’s time controls carry its own name..time-picker-roll-coland.time-picker-roll-cellare.time-picker-coland.time-picker-cell, with--cui-time-picker-col-*and--cui-time-picker-cell-*in place of their-roll-spellings;.time-picker-rollwas always present on.time-picker-body, so it is gone rather than renamed, and--cui-time-picker-body-paddingwent with it..time-picker-inline-iconand.time-picker-inline-selectare.date-picker-time-iconand.date-picker-time-select, with--cui-date-picker-time-*tokens, because they belong to the date picker’s panel. Custom CSS written against any of these names needs the new one. -
Breaking
CTimePicker:variantis gone. Each picker has one time rendering — the time picker shows the roll, and the date picker’s time half shows the selects, which is what v5 rendered inside a date picker with time. The selects ship in the date picker stylesheet, so a page that only uses the time picker no longer downloads them. -
Breaking The time selection is one tab stop. Tab reaches the body once, Arrow Left and Arrow Right move between the hour, minute, second and AM/PM parts and stop at the ends, and Arrow Up / Arrow Down change the focused value. Both renderings answer the same keys.
-
Breaking
CTimePicker:ampmis gone. The hour cycle follows the field’s mask when it names one (HHvshh a) and the locale otherwise. -
Breaking
closeOnSelectandpreviewDateOnHoverare gone. A complete selection closes the panel unless afooterprojects actions the user still needs. -
Breaking
indicatoris gone. The button that opens the panel is always rendered;pickerIconreplaces its artwork. The button that clears the value is the one you can switch off, withcleaner. -
Breaking The v1 shell class names are gone:
date-picker-input,date-picker-input-wrapper,date-picker-input-preview,date-picker-cleaner,time-picker-input,time-picker-cleaner, and the-dropdownpanels are now.date-picker-popup/.time-picker-popup. -
The panel is rendered through a portal by default (
portal), intocontainerwhen given. It is built on the first open, not on mount. -
New props across the family:
cleanerandcleanerIcon,pickerIcon,disabled,visible,onShow/onHide,size, and the accessible labelsariaCleanerLabel/ariaPickerLabel. -
Pickers open on the platform’s dropdown keys.
Alt+ArrowDownandF4open the panel and move focus into it — onto the selected date, else today or theminDate/maxDatetoday falls outside of, also when that date cannot be picked. The field’s own arrows keep editing the value, as<input type="date">does. Opening with the mouse moves focus in too.
CDropdown
- Placements are mirrored once now, by Floating UI. The
-start/-endhalf of a placement is logical — Floating UI resolves it from the writing direction of the floating element — and the library was mirroring it a second time, which cancelled it. In a right-to-left context that moves every placement whose suffix you set:alignment="end"now hangs from the inline end, and an explicitplacement="bottom-start"opens from the right edge instead of the left. The sides are the other way round: Floating UI never mirrorsleft/right, so a logicalstart/endside still maps to the physical one the direction asks for anddropend/dropstartstill swap. Also in this change: adropupaligned to the end keeps its side (it resolved tobottom-endbefore),direction="center"resolves tobottomrather thanbottom-start, a submenu in a right-to-left context anchors to the top of its trigger rather than the bottom, anduseDropdownWithFloatingasks Floating UI for a plainbottom-start.CMenu,CSubmenu,CContextMenuand the picker panels all resolve a placement the same way.CTooltipandCPopoverkeep mirroring a physicalleft/rightas a v5 compatibility fallback, which is removed in v7. - A panel sent through a portal carries the direction of its trigger. Out in
document.bodyit no longer inherits the direction that resolves the suffix, so it would have been laid out as left-to-right while its trigger sat in adir="rtl"subtree. Adiryou set yourself on the panel is left alone. CDropdownis now a compatibility layer overCMenu. The v5 markup, class names, props and callbacks keep working — the two share one engine underneath, so the dropdown also gainedEscapeclosing regardless ofautoClose. Deprecation notice:CDropdownmay be removed in v7 — preferCMenufor new work.- Breaking
popperConfig→floatingConfig, see Dependencies.
CFocusTrap
CFocusTrap’sadditionalContaineraccepts an element, not only a ref. A ref is read once, when the trap activates, which is too early for a panel that is built on open.
CFooter
- Breaking
CFooteris removed. It rendered a.footerclass whose nine declarations utilities already cover, so a footer is plain markup now:<footer className="d-flex flex-wrap align-items-center justify-content-between bg-3 border-top px-5 py-3">. Themin-height: 3remfloor has no utility — set it yourself where it matters, or pad withpy-4. - Breaking The
positionprop goes with it. Use.sticky-bottomin place ofposition="sticky"(it pins the same way, on--cui-z-stickyrather than--cui-z-fixed) and.fixed-bottomin place ofposition="fixed", which emitted a.footer-fixedclass the PRO stylesheet has never defined.
CForm
validatewithnoValidatevalidates the form on submit.<CForm>stops an invalid submit (onSubmitruns only for a submit that goes through), marks the invalid controls with.is-invalidandaria-invalid, links the message of each to it witharia-describedbywhile it stays invalid, and moves focus to the first. A hand-writtenonSubmitthat only calledcheckValidity()andpreventDefault()can go. The newonValidatecallback runs first; callpreventDefault()on its argument to handle the result yourself.
CFormCheck
- Breaking
hitAreais removed. It rendered the label as astretched-linkso a whole list group item would toggle the box; the list group item can be the label itself now —<CListGroupItem as="label"><CCheckbox className="me-1" /> Text </CListGroupItem>— which is what the stylesheet is written for.
CFormFloating
- Breaking The placeholder shows once the field has
focus. It used to stay transparent in every state; it now appears under the floated label while
the field, or the control inside a field component, has focus, and stays hidden while the label
rests in the field. A
plainTextcontrol keeps it hidden. - New:
alwayson<CFormFloating>andfloatingLabelAlwayson every form control that renders a floating label keep the label floated and the placeholder showing while the field is empty. See Always floating.
CFormLabel
- Breaking
customClassNameis removed. It was the one prop in the library that replacedclassNameinstead of adding to it, and onlyCFormCheckandCFormSwitchused it internally to render a label without.form-label; they render a plain<label>now. PassclassNamefor extra classes; a label without the base class is a plain<label>element.
CFormRange
- Changed. A pressed thumb takes
--cui-primary-bg-muted, the theme’sbg-mutedunder a.theme-*class, instead of a 30% tint of the primary color. - Breaking
<CFormRange>renders a wrapper. The input sits inside<div class="form-range">as.form-range-input;className,style,theme,dirandhiddenland on the wrapper, which carries the tokens, while every other prop still reaches the input andrefstill points at it. Alabelwithout anidgets a generated one, so the label names the input. The component writes--cui-range-fill(0–1) from the value, so the track fills up to the thumb, a shade lighter than it, in every browser. An uncontrolled range seeds fromdefaultValue(the native midpoint without it). tooltipsandtooltipsFormatshow the value above the thumb, on hover, drag and focus or"always";ticksrenders tick marks from an array of values, labels or{ value, label, className, style }objects, labels beneath. Both follow the vanillaRangeplugin, which also reads a<datalist>, and share names with<CRangeSlider>.onChangegets the value as a number in its second argument,onChange(event, value). Several handles,distance, clickable ticks and a vertical orientation are what<CRangeSlider>adds.
CHeader
- Breaking
positionrenders position utilities.position="sticky"emits.sticky-topin place of.header-sticky, which the stylesheet no longer defines — the two rules were identical, so the header pins exactly as before.position="fixed"emits.fixed-top; the.header-fixedclass it used to render was never defined anywhere, so that value had no effect at all. For the bottom variants, or a responsive one like.sticky-md-top, pass the utility throughclassNameinstead.
CInputGroup
-
Breaking Validation feedback moves out of the group. The
.has-validationclass is gone, and a group keeps its corner logic by having the control as its last child — aCFormFeedbackleft inside squares off the end of the control it belongs to. Wrap the group in aCFormFieldand put the feedback after it, so the message renders under the whole frame. Inside a group that means a standaloneCFormFeedbackrather than thefeedback,feedbackValidorfeedbackInvalidprops, which render it next to the control.<CFormField> <CInputGroup> <CInputGroupText>@</CInputGroupText> <CFormInput validationState="invalid" required /> </CInputGroup> <CFormFeedback validationState="invalid">Please choose a username.</CFormFeedback> </CFormField>
CLoadingButton
spinnerTypeis deprecated in favour ofspinnerVariant— the same wordCSpinneruses forborder/grow; the old name loses to the new one.onClickreceives the click event, as onCButton, instead of nothing.- The spinner takes the button’s font size. It no longer asks
CSpinnerforsize="sm"— a spinner is1emsquare in v6, so inside a button it already lands where the small size used to put it. Custom CSS targeting.btn-loading-spinner.spinner-border-smor.spinner-grow-smhas nothing to match; the sizing classes are gone from that element. - Breaking
disabledOnLoadingno longer renders thedisabledattribute. A disabled button drops focus, so a keyboard or screen reader user who pressed it landed on the page body. While loading, the button now getsaria-disabled="true"and.disabled, keeps focus, and ignores clicks and form submission until it stops. CSS or tests that read:disabledon a loading button should read[aria-disabled="true"]instead. - The start and the end of loading are announced when the button has focus: its name followed
by the new
ariaLoadingLabelprop, then its name again. The spinner no longer rendersrole="status"or the “Loading…” text that Safari read on an idle button; while loading it is arole="img"named byariaLoadingLabel.
CMenu
- New:
customonCMenuToggle. With it the single child element receives the toggle behaviour —aria-expanded,aria-haspopup, the click handler and the ref — and keeps its own markup and classes, instead of the toggle rendering aCButton.CDropdownTogglehas carried the same prop since v4; this closes the gap in the component that replaces it.
CModal, COffcanvas
- Breaking Both render a native
<dialog>. The browser provides the top layer, the::backdrop, focus trapping, background inertness and the dialog ARIA semantics, so the markup dropsrole,aria-modal,aria-hiddenandtabIndex={-1}, and the component no longer renders a backdrop, a focus trap or a portal of its own. - Breaking
CModalDialogandCModalContentare removed..modal > .modal-dialog > .modal-contentflattens to a single<dialog className="modal">holding the header, body and footer directly.CModalrendered both for you, so markup written against the components is unchanged; custom CSS targeting those two classes is not. - Breaking
portalandcontainerare gone. They existed to lift the modal out of a stacking context. A dialog opened withshowModal()renders in the top layer, above every stacking context on the page, so there is nothing left to escape. - Breaking
focusis gone — a modal dialog always traps focus, and the option could only have turned off something the browser does. - Breaking
alignmentis gone — the browser centers a modal dialog.alignment="top"has no replacement; a dialog that must sit at the top is a positioning override in your own CSS. - Breaking
.modal-dialog-scrollableis.modal-scrollable, and.modal-backdrop/.offcanvas-backdropare gone with the elements they styled — the backdrop is the::backdroppseudo-element, themed through--cui-modal-backdrop-*and--cui-offcanvas-backdrop-*. - Breaking Animation opt-out is a class now.
transition={false}renders.modal-instant/.offcanvas-instantinstead of dropping.fade; the offcanvas.showingstate class is replaced by the nativeopenattribute, and.hidingcarries the exit. - New:
modal={false}onCModalopens the dialog non-modally, leaving the rest of the page interactive. New:animationonCOffcanvas. - Scroll locking moved to the root element —
dialog-openwithscrollbar-gutter: stable, instead of inlineoverflowandpadding-righton the body.
CNavGroup
- The stylesheet owns the group animation where the browser can interpolate a size to
auto.CNavGroupno longer writes an inlineheight/displayon.nav-group-items; the run is marked with.collapsingand the open state with.showon the group, and the duration and easing come from--cui-sidebar-nav-group-items-transition-*. Browsers withoutinterpolate-sizekeep the measured path. Custom CSS keyed on the inline height needs updating. - A group that renders open no longer plays the opening animation on mount —
defaultShow, a controlledshowand an active nav link all reveal the branch straight away.
COneTimePassword
- Breaking Only
COneTimePassword’s hidden input carries the field’sname. EachCOneTimePasswordInputwas given<name>-<index>on top of it, so a six-slot field submitted seven keys where the prop is documented as the name of the hidden input. The slots are unnamed now, and anameyou pass to a slot is left alone instead of being overwritten. - Breaking The code is validated as one value. It
moved from an
<input type="hidden">, which the browser leaves out of validation, to a text input laid invisibly over the group, withtabIndex={-1},aria-hidden="true", apatternfor the full length of the code and therequiredthat the slots used to carry. Arequiredcode that is empty or incomplete stops the submit, so does an incomplete code that is not required, and the browser puts focus on the first empty slot. Once the browser reports the code, every slot carriesaria-invalid="true"and the error message while the code stays invalid, as it already did forvalidationState="invalid", and the stylesheet shows the message after the group, which it never did for a slot marked invalid. Code that selectsinput[type="hidden"]inside the field needs.form-otp > input:not(.form-otp-control), and adata-coreui-invalid-feedbackwritten on the slots goes onCOneTimePassword. - Breaking
COneTimePasswordblocks the submit whilevalidationState(or the deprecatedinvalid) is'invalid', and asks to drop the state withonValidationStateChange(undefined)when the user changes the code or the form is reset. The state class moved from eachCOneTimePasswordInputto the.form-otpgroup, which the stylesheet colours as a whole, so CSS or tests keyed on.form-otp-control.is-invalidneed.form-otp.is-invalid. Anaria-invalidpassed to the component or to a slot is no longer put on the group or the slot, the hidden input sendsinputandchangewhenever the code changes, also throughvalue, and a native form reset puts back the code given bydefaultValue. See Validation. - Breaking The
readOnlyprop is removed, and every slot is editable. A code the user cannot type has no use in a form, so areadOnlyonCOneTimePasswordor on aCOneTimePasswordInputno longer makes a slot read-only; usedisabledto show a code the user cannot change.
CPasswordInput
- Breaking Renamed
ariaLabelTogglertoariaToggleLabel. - Breaking The toggle icon is rendered by the component
instead of being drawn by CSS, and is replaceable through the new
showIcon/hideIconprops..form-password-action-iconis gone, the frame is.form-control-groupand the button is.form-control-action.
CPlaceholder
animation="wave"is a sweeping highlight and the recommended loading animation. A narrow bright band crosses the placeholders every 1.6s instead of a wide gradient every 2s, and it also works on text. The opacity pulse isanimation="pulse"now;animation="glow"stays as its alias. The prop still has no default. The keyframes are renamed toplaceholder-pulsewith it. With reduced motion the wave stops and drops its mask.- Placeholders are paler, taller and rounded. The resting opacity is
.18(was.5) and the pulse dips to.1(was.2); the default height is1.25em(was1em), capped a quarter em below the line height so a placeholder never makes a heading taller, and.75em,1emand1.75emfor thexs,smandlgsizes (were.6em,.8em,1.2em). Each placeholder has a--cui-radius-3corner; aCButton,CAvatar,CBadgeorCChipwith theplaceholderclass keeps its own height and corners. The wave reads its own--cui-placeholder-wave-opacity-maxand-min.
CPopover, CTooltip
onShowandonHidefire when the tip starts showing or hiding, afterdelayand only when the visibility actually changes.onShowused to fire on the trigger gesture, beforedelay.show, and again for a second trigger on a visible tip, whileonHidealso fired after a hover that ended before the tip appeared.onShowChangereports the requests; a deprecatedvisiblekept in step throughonShowandonHidebelongs onshowandonShowChange.- New:
onShown/onHidden, fired once the entry or exit transition has finished.
CProgress, CProgressBar
- Breaking
thinis removed fromCProgress. It rendered.progress-thin, which v6 does not ship; setheightfor a slimmer bar. - Breaking
colorno longer takes a gradient. v6 removes thebg-*-gradientutilities the value mapped to, so theGradientsunion is gone from the prop’s type. Reach for.bg-gradientalongside the color if you want the sheen.
CRangeSlider
- Breaking The slider renders the markup of the
range. It is a
<div class="form-range">with one native.form-range-inputper handle;className,style,theme,dir,hidden,role,aria-label,aria-labelledbyand therefland on thatdiv, and other input attributes, such asid(on the first handle) andaria-describedby, reach the handles. The classes follow:.range-slider→.form-range,.range-slider-input→.form-range-input,.range-slider-vertical→.form-range-vertical,.range-slider-tooltip→.form-range-tooltip(now a<div>),.range-slider-ticks/.range-slider-tick→.form-range-ticks/.form-range-tickwith the text in.form-range-tick-label, and the per-tick.clickable→.form-range-ticks-clickableon the container;.range-slider-inputs-container,.range-slider-trackand the.disabledclass are gone. The handles no longer carryrole="slider",aria-valuemin,aria-valuemaxoraria-valuenow: the native inputs expose them. - Breaking The handles and the track read the range’s
tokens.
--cui-range-slider-*is gone: set--cui-range-thumb-*,--cui-range-track-*,--cui-range-track-fill-bg(was-track-in-range-bg),--cui-range-track-disabled-bg(was-disabled-track-in-range-bg) and--cui-range-tick-label-colorand-font-size(were--cui-range-slider-label-*) on the slider. The label padding has no counterpart, the tooltips sit like the range’s without--cui-range-slider-tooltip-margin-*, and a vertical slider’s height is--cui-range-vertical-heightand its track width--cui-range-track-height. - Breaking
ariaLabelsisariaLabel, an array with one name per handle or a function(index, total) => string. - The slider is a group named after the field. The
.form-rangegetsrole="group"with thearia-labeloraria-labelledbyyou pass, so a screen reader announces the field when the focus enters it. With several handles, each handle carries only its own name (“Minimum value”); a single handle takes the name of the group.aria-describedbydescribes every handle, and aroleyou pass replacesgroupon the slider. - The slider takes a validation state.
validationStateshows on the slider and on the handles, and'invalid'blocks the submit through the first handle until the user moves a handle or the form is reset; the slider callsonValidationStateChange(undefined)then. See Validation. - Breaking
labelsisticks,clickableLabelsisclickableTicks. An entry is a number (a tick without text), a string (a label placed by its position), or{ value, label, className, style }; a bare element is no longer an entry, pass it as{ label: <strong>High</strong> }. - Interaction follows the native control. Pressing a handle no longer moves it; a press on the
track or a clickable tick moves the nearest handle and it follows the pointer (mouse, touch or
pen); a form reset restores
defaultValue. A controlled slider whose parent ignoresonChangekeeps the handle on its value instead of letting it follow the pointer until the drag ends. tooltipstakes"always".trueshows a handle’s tooltip on hover, drag and focus;"always"keeps them on.
CRating
- A read-only rating submits its value under the
nameyou give it.readOnlyused to disable the radios, so the form left the value out; now they stay enabled, the mouse and the keyboard cannot change them, only the checked star takes focus (the first star while no star is checked), and the group carriesaria-readonly="true". A read-only rating withoutnamestill submits nothing. - A star with keyboard focus shows the focus ring.
- A read-only rating shows no tooltips.
- The rating takes a validation state and can be required.
validationStatecolours the focus ring, and'invalid'setsaria-invalidand blocks the submit until you drop the state; the rating asks for that withonValidationStateChange(undefined)when the user picks or clears a star or the form is reset.requiredmakes every radio required. See Validation. - Empty stars are
--cui-fg-4: gray-500 in the light scheme and gray-600 in the dark one, the lightest grays that keep 3:1 against the page background. The empty stars of v5 were 38% of the text colour, below 3:1 against the page in the light scheme.
CSidebar
onVisibleChangeis deprecated in favour ofonInViewportChange. The callback reports whether the sidebar is on screen — it is off-canvas on narrow viewports — not theshowstate, and the old name read like the latter’s twin. Both fire while the old name is set.colorScheme="light"joinscolorScheme="dark". Either value pins the sidebar to one scheme whatever the page uses, so a dark navigation column can sit on a light page.- Theme variants.
className="theme-{color}"paints the surface, text, icons and the hover/active links from that colour’s slots. - Hover and active links read the foreground ladder. Their text was the pure black/white
--cui-emphasis-colorand their background the Sass-computed--cui-tertiary-bg-translucent; they are--cui-fg-bodyandcolor-mix(in srgb, var(--cui-fg-body) 10%, transparent)now. Slightly stronger than before on a light sidebar, slightly weaker on a dark one.
CSmartTable
-
Breaking The props are renamed. Every filter prop says which filter it means, the column configuration drops the v3 underscores and the sorter icons take the names the other data components use:
v5 v6 elementCoverloadingContentitemsNumbertotalItemsonSelectedItemsChangeonSelectedChangesortingIcon,sortingIconAscending,sortingIconDescendingsortNeutralIcon,sortAscendingIcon,sortDescendingIcontableFilter,tableFilterValue,defaultTableFilterValue,onTableFilterChangeglobalFilter,globalFilterValue,defaultGlobalFilterValue,onGlobalFilterChangetableFilterLabel,tableFilterPlaceholderglobalFilterLabel,globalFilterPlaceholdercolumnSorter={{ resetable: true }}columnSorter={{ resettable: true }}Column._style,Column._propsColumn.headerStyle,Column.headerPropsColumn._colStyle,Column._colPropsColumn.cellStyle,Column.cellPropsColumn.sorter: falseColumn.sortable: false(sorteris the comparator only)Column.filter: falseColumn.filterable: false(filteris the custom filter only)FooterItem._propsFooterItem.propsonSelectAll— ( onSelectedChangecarries the same information) -
Breaking A column group is
{ label, children }. The group label moves fromgrouptolabeland a group no longer needs akey:{ group: 'Address', key: 'address', children: [...] }becomes{ label: 'Address', children: [...] }. -
Breaking One
externalprop replaces the sixexternaloptions.columnFilter,tableFilter,columnSorter,pagination,itemsPerPageSelectandselectAlleach carried anexternalflag;externalon the table says the same thing once — the items arrive already filtered, sorted and sliced, so the table renders them as they are and only reports the requested state. Pair it withtotalItemsso it can compute the number of pages. Where the option only meant “let the parent own this piece of state”, pass the controlled prop instead. -
Breaking
SorterValueis{ column, direction }and the sorter value is always an array.state: 'asc' | 'desc' | 0becomesdirection: 'asc' | 'desc', with an absent entry standing for “not sorted”, andsorterValue,defaultSorterValueand theonSorterChangepayload areSorterValue[]whether or notcolumnSorter={{ multiple: true }}is set — no moreArray.isArrayat the call site. -
Breaking
Item._selectedis removed. Seed the selection withdefaultSelected, or own it withselected— the flag in the data was a second source of truth and the two drifted apart. -
The selection checkboxes and the cleaner button have accessible names.
ariaSelectAllLabel('Select all rows'),ariaSelectRowLabel('Select row') andariaCleanerLabel('Clear filters and sorting') are read by screen readers; before, none of the three controls had a name at all. -
itemKeyidentifies a row. With it set, selection and React keys compareitem[itemKey]instead of the whole object, so a selected row survives a re-fetch of the data. -
cleanerIconreplaces the cleaner button’s icon, the way the sorter icons already could. -
The selection checkboxes render
CCheckbox. The select-all control in the head and the per-row control in the body are<input class="check">now, not.form-check-input, so custom CSS keyed on the old class needs updating. -
The chrome is composable.
CSmartTablerenders the layout it always did as long as it has no children; pass children and it renders them instead, soCSmartTableFilter,CSmartTableCleaner,CSmartTableContent,CSmartTablePaginationandCSmartTableItemsPerPagego wherever you put them, next to your own markup.useSmartTableContext()reads the same state — the items, the page, the filters, the sorter and the selection — so a part of your own is a component that calls it. -
useSmartTable(options)runs the table without any of its markup. The whole state — the filters, the sorter, the pages, the selection withisSelected,toggle,toggleAllandselectedAll— is a hook you can call with the state props ofCSmartTableand render however you like, tables included or not.CSmartTableitself is that hook plus the layout.
CSpinner
-
visuallyHiddenLabelis deprecated in favour ofariaLoadingLabel, which works asariaLoadingLabeldoes onCLoadingButton: the text defaults to'Loading'instead of'Loading...', and an empty string hides the spinner from screen readers.visuallyHiddenLabelstill sets the text whileariaLoadingLabelis unset. -
Spinners are sized in
em, notrem. A spinner is1emsquare now, so it takes the font size of whatever it sits in instead of being a fixed 32 px block. Inside a button or a heading it lands at the size you previously had to ask for withsize="sm"; standalone it is half of what it was. Set--cui-spinner-width/--cui-spinner-heightto go back to a fixed size. -
size="sm"renders.spinner-sm, not.spinner-border-sm/.spinner-grow-sm. The prop is unchanged; only custom CSS targeting the old per-type classes needs updating. -
.spinner-groweases withease-in-outinstead of pulsing linearly. Override--cui-spinner-animation-timing-functionto change either spinner’s easing. -
.spinner-rotateis new. Pass it throughclassNameon any element to spin your own artwork without inheriting the bordered circle.
CStepper
-
Breaking
--cui-stepper-step-indicator-active-bgis removed. The active step’s indicator background is a 5% tint of the stepper’s theme color, or of--cui-stepper-step-indicator-active-colorwithout one. To give it another background, set--cui-stepper-step-indicator-bgon.stepper-step-button.activein your stylesheet. -
Breaking
activeStepIndexanddefaultActiveStepIndexare removed. Deprecated since 5.16.0 for their 0-based indexing; useactiveStepNumber/defaultActiveStepNumber, which count from 1. -
A failed step moves focus to the first invalid control, and the panes are no longer live regions. With a
noValidateform the invalid controls also getaria-invalid, and the message is read with the field: the stepper adds the.invalid-feedbackthat follows the control, which coversfeedbackInvalid, or the elements named indata-coreui-invalid-feedback, to itsaria-describedby. The panes used to read out every message that appeared in them; a message that changes while the user types is now silent, so pass the ones that have to be heard toannounce(). -
onStepValidationCompleteruns before the stepper decides. A validity it sets withsetCustomValidity()blocks the step, andisValidtells whether every control passed before it ran. AnoValidatestep form is marked each time the step is validated, also when it passes, sodata-coreui-validate="valid"now marks the valid controls of a passing step with.is-valid. -
A non-linear stepper validates the step being left. With
linear={false}, clicking a later step used to skip validation; it now runs it likenext()does and stays put while the step is invalid. Going back is never blocked. Addvalidation={false}for the old behaviour.
CTable
- Breaking
captionis the caption andcaptionTopis a boolean.caption="top"no longer carries a magic value andcaptionTopno longer carries text:<CTable caption="top"><CTableCaption>…</CTableCaption></CTable>becomes<CTable captionTop>…</CTable>, and<CTable captionTop="List of users" />becomes<CTable caption="List of users" captionTop />. - New:
cardwraps the table in a card’s border and corner radius, clips its cells and drops its bottom margin and the borders that would stack on the frame. Withresponsivethe class lands on the same wrapper. See Card-like tables.
CToast, CToaster
-
Breaking
CToaster.pushis gone; toasts come from a store.createToaster({ placement, limit, timeout })builds the store outside the React tree and<CToaster toaster={toaster} />renders it:add(options)returns the id (an existing id updates in place and bumpsupdateKey, a closing one is replaced),update(id, options | fn(prev))rewrites a toast,close(id?)hides one or all,promise(p, { loading, success, error })follows an asynchronous operation. A toast is options —title,description,theme,timeout(0= sticky),priority('normal'or'high', the polite or the assertive live region),dismissible,action,render— instead of a<CToast>element.limitkeeps the newest toasts visible and leaves the oldest in the DOM asinertand hidden.useToaster()returns the store inside the container.<CToaster>withouttoasterstays a container for<CToast>children.// v5 const [toast, addToast] = useState() <CButton onClick={() => addToast(<CToast><CToastBody>Saved</CToastBody></CToast>)} /> <CToaster placement="top-end" push={toast} /> // v6 const toaster = createToaster({ placement: 'top-end' }) <CButton onClick={() => toaster.add({ description: 'Saved' })} /> <CToaster toaster={toaster} /> -
Breaking
CToastdropsindexandinnerKey, andonClose/onShowfire without an argument — they were the plumbing ofpush, and a toast rendered by the store is identified by itskeyanddata-coreui-toast-id. -
Placement is one class.
<CToaster placement>renders.toast-container-{placement}in place of theposition-fixed top-0 end-0utility stack; the edge inset comes from--cui-toast-container-inset. The container is aregionlandmark namedNotifications. -
The store carries the rest of the behavior.
ariaLabelnames the landmark,enterandleavepick the edges a toast slides in from and out to,pauseOnHover(on by default) stops every timer while the pointer or focus is inside the container, andstack: truepiles the toasts up newest-in-front and spreads them out on hover.<CToaster portal={false}>keeps the container where it is written instead of portalling it ontodocument.body. -
Announcements moved off the toast. A toast rendered by the toaster carries no
role,aria-liveoraria-atomic; everyadd()andupdate()passes the string parts oftitleanddescriptiontoannounce(), which reads them at the prioritypriorityselects. A standalone<CToast>keeps its ownrole="alert"defaults. -
New:
translucentrenders.toast-translucent— the background shows through the toast, blurred, and the header thins out with it. -
The fade moved to CSS. The toast no longer renders the
fadeandshowinghelper classes and no longer drives its own opacity —showalone drives the transition, with@starting-stylesupplying the state to animate from and a discretedisplaytransition keeping the toast laid out until the fade-out finishes.animation={false}now renders.toast-instantinstead of droppingfade, and finishes both directions immediately. The prop itself is unchanged; only custom CSS targeting.toast.fadeor.toast.showingneeds updating. Tune the motion with--cui-toast-transition-durationand--cui-toast-transition-timing.
CWidgetStatsA – CWidgetStatsF
- Breaking The six widget components are removed. They
were the one family in the library with no stylesheet of their own: each rendered a
CCardand a fixed stack of utility classes, so they carried a layout decision rather than styling you cannot write in place. Compose the same markup directly, which also lets a widget diverge from the six shapes we happened to ship:
// Before
<CWidgetStatsF color="primary" icon={<CIcon icon={cilSettings} height={24} />} title="Users" value="87.500" />
// After
<CCard>
<CCardBody className="d-flex flex-row align-items-center">
<div className="me-4 fg-white bg-primary p-4">
<CIcon icon={cilSettings} height={24} />
</div>
<div>
<div className="fs-6 fw-semibold fg-primary">87.500</div>
<div className="fg-2 text-uppercase fw-semibold small">Users</div>
</div>
</CCardBody>
</CCard>- The six shapes live on as blocks. Every one of them is on that page as
markup to copy, with the names they go by now:
CWidgetStatsAis the stat with a chart,CWidgetStatsBthe stat with a progress bar,CWidgetStatsCthe one with an icon and a progress bar,CWidgetStatsDthe stat group,CWidgetStatsEthe compact stat andCWidgetStatsFthe stat with an icon. - Three of them did not survive the v6 stylesheet unchanged, which is the other half of the
reason they are going.
CCardBodybecame a flex column in v6, soCWidgetStatsAstacked its menu under the figure instead of putting it in the corner,CWidgetStatsFstacked its icon above the text, andCWidgetStatsDstacked its values instead of laying them in a row. The blocks carry theflex-rowandw-100those layouts now need.