CoreUI & Tailwind CSS
How to run CoreUI for React components next to Tailwind's utilities: one cascade layer order, no second reset, and Tailwind reading our design tokens.
Two libraries, one cascade
CoreUI ships its utilities as a separate stylesheet, so a project that prefers Tailwind’s utilities can leave ours out entirely and keep the components. What has to be agreed between the two is the cascade: both libraries put their rules in named @layers, and two of the names are the same.
CoreUI declares:
@layer colors, config, root, reboot, layout, content, forms, components, custom, helpers, utilities;Tailwind 4 declares:
@layer theme, base, components, utilities;Layers with the same name are one layer, and the browser fixes the order of every layer at its first mention. Import Tailwind first and CoreUI’s root, reboot and forms land after Tailwind’s utilities, so a .p-4 loses to Reboot. Import CoreUI first and Tailwind’s base — its reset, Preflight — lands after our forms and utilities, and undoes them on every element it touches. Neither order works on its own.
The fix is one line, written by you, before either stylesheet: a single @layer statement that interleaves the two lists. The shared components and utilities names merge, which is what you want — Tailwind’s @utility additions sit in the same layer as ours, and your @layer components in the same layer as our components.
/* app.css */
@layer theme, base, colors, config, root, reboot, layout, content, forms, components, custom, helpers, utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/utilities.css" layer(utilities);
@import "@coreui/coreui-pro/css/base.css";The component stylesheets are not listed: every component imports its own when it is imported, and each of those files opens with CoreUI’s @layer declaration. That is why app.css has to be the first stylesheet the bundler meets — before any component import — so that the interleaved order is the one that sticks:
// main.tsx
import './app.css'
import { CButton } from '@coreui/react-pro'Two decisions are baked into app.css:
- One reset, not two.
@import "tailwindcss"also brings Preflight; the two explicit imports above bring Tailwind’s theme and utilities and leave the reset out, becausebase.cssalready carries Reboot. Two resets on one page never agree on form controls and headings. - CoreUI’s utilities stay out.
coreui-utilities.cssis not imported. Nothing in a component depends on it, so the components render the same. If you do want both sets, import ours too — they share theutilitieslayer, and the later file wins on a name both define. That includes the numbered spacing classes:p-4is.75remin CoreUI and1remin Tailwind, and only steps0,1and12give the same value in both.
Class names both libraries use
Sixty-odd class names exist in both utility sets — flex, border, shadow, rounded, p-1, text-center — which stops mattering the moment our utilities are not loaded. Four names are CoreUI component or layout classes that Tailwind also defines as utilities:
| Class | CoreUI | Tailwind |
|---|---|---|
.grid | the CSS grid container: twelve columns and a gap | display: grid |
.container | the responsive container with its max-widths | a max-width container of its own |
.table | the table component | display: table |
.collapse | the collapse component | border-collapse: collapse |
.table and .collapse are harmless — Tailwind’s declaration is one the component already carries. .grid and .container are not: writing Tailwind’s grid on an element gives it our twelve-column template and gutter. Either leave layout/grid.css and layout/containers.css out when you use Tailwind’s, or give Tailwind a prefix so its classes cannot meet ours (the layout/grid.css file is what CRow/CCol import, so the prefix is the way when you use those):
@import "tailwindcss/theme.css" layer(theme) prefix(tw);
@import "tailwindcss/utilities.css" layer(utilities) prefix(tw);Tailwind’s classes are then written tw:grid, tw:p-4, and no name is shared.
Tailwind reading CoreUI’s tokens
Tailwind’s utilities take their values from its @theme block, and a theme value can be a var(). Point the entries at CoreUI’s tokens and bg-primary, p-4 and rounded-md follow our palette, spacing scale and radius — including the color mode, because our tokens resolve through light-dark():
@theme {
--color-primary: var(--cui-primary-base);
--color-primary-fg: var(--cui-primary-fg);
--color-success: var(--cui-success-base);
--color-danger: var(--cui-danger-base);
--color-body: var(--cui-bg-body);
--color-fg: var(--cui-fg-body);
--spacing: var(--cui-spacer-1);
--radius-sm: var(--cui-radius-2);
--radius-md: var(--cui-radius-3);
--radius-lg: var(--cui-radius-5);
--font-sans: var(--cui-font-sans-serif);
}--spacing is Tailwind’s multiplier — p-4 is four times it — and --cui-spacer-1 is a quarter of --cui-spacer, so Tailwind’s spacing follows --cui-spacer. The step numbers do not line up with ours, so pick one set of spacing classes per page. Override --cui-primary-base on :root and both libraries repaint. Every token that can be mapped is listed on the CSS variables page.
The other direction needs nothing: a component reads its color from the theme slots on the element, so <CButton theme="primary"> is themed the same way whether Tailwind is on the page or not, and a .theme-brand class of your own works next to Tailwind’s bg-* classes.
What CoreUI’s components never need
The components do not use utility classes internally, so leaving coreui-utilities.css out cannot break one. The classes our JavaScript toggles — show, active, collapsing, disabled — are component states that live in the component stylesheets, not utilities, and they are unaffected by Tailwind’s content scanning: they are not in your markup, and they do not need to be.