opsinjs
IntroductionStart here

Framework support

Which React setups opsinjs targets, what it requires of them, and where the server/client boundary falls.

Overview

opsinjs is React-only, and there is no plan for a Vue, Svelte or web-component build. The tokens are plain CSS custom properties and are reusable anywhere; the components are not, and pretending otherwise would produce a worse React library.

Within React the question is not really "which framework" but "which four capabilities does your setup have". Those four are what this page is about.

The tested matrix is not published yet

Every catalogue row is implemented and installable, and none of the rows below has a recorded test result: no framework in the matrix has been exercised end to end and the result written down. The matrix fills from generated results once there are any. What is stated here instead is the requirement set and the boundary rules, which are design decisions and are true now.

Meet four requirements

Everything else follows from these. If your setup has all four, opsinjs is intended to work; if it is missing one, the gap is usually specific and fixable.

React 19 or later

Not negotiable. opsinjs is built on Base UI, which targets modern React, and several components depend on React 19 form and ref behaviour. React 18 is not supported and will not be back-ported.

Tailwind CSS v4

Also not negotiable. The token layer is authored as CSS custom properties consumed through Tailwind v4's @theme mechanism, and there is no tailwind.config.js to port. Tailwind v3 users need to upgrade first; Tailwind v4 covers the parts of that upgrade that interact with opsinjs.

A bundler that can scan your source for classes

Tailwind v4 discovers classes by scanning declared sources. Because opsinjs components are copied into your repository rather than installed as a package, the directory you copy them into must be scanned. This is the single most common setup failure and it produces a component that renders with no styling at all. See Monorepo for the @source lines.

A place to import one stylesheet, in a known position

Order is load-bearing. The opsinjs token layer must come after Tailwind and after any base-layer reset, and before your own overrides. Each installation page states the exact position for that framework.

Know where the client boundary falls

This is the part that differs most between frameworks, and the part where an incorrect assumption costs a day.

Many opsinjs components are server-renderable. Twenty-seven of the sixty carry no "use client" directive at all. A ResultCard showing a value, a range and a status is static markup; there is no reason for it to ship JavaScript, and it does not.

The rest require a client boundary, and each one says so on its own page in its cost section. Thirty-three of the sixty carry "use client" today, among them Sheet, Dialog, ConsentSheet, LogSheet and ReadingInput, which manage focus and open/closed state; Term, whose plain-English expansion is a disclosure the reader opens; and RelativeTime, for the reason below. TrendSparkline is not among them. It renders as static markup, so a series is safe in a server tree. Where a component is split, the interactive part is a separate file, so importing the display half does not drag a client boundary into your server tree.

Theme and density are CSS, not context. There is no provider to mount for theming: the mode is a class and the tokens are custom properties. That is what makes server rendering practical, and it is why a flash of incorrect theme is a solved problem rather than a known issue.

RelativeTime is the honourable exception worth knowing about in advance: "3 hours ago" cannot be computed identically on a server and a client without care, and its page documents the hydration-safe contract rather than leaving you to discover the mismatch.

Frameworks in scope

SetupIntended statusWhat is specific about it
Next.js App RouterPrimary targetThe reference install; server components by default. Guide
Next.js Pages RouterSupported, not primaryNo server components; everything is a client tree. Global CSS position still matters.
Vite + ReactSupportedClient-only. The @source gotcha bites hardest here. Guide
React Router framework modeSupportedServer rendering with a different data story. Guide
TanStack StartIntendedGuide
Astro with React islandsIntended, with caveatsPer-island hydration changes the boundary calculus. Guide
Remix (pre-React-Router-7)Not targetedUse the React Router framework-mode path after upgrading.
Create React AppNot targetedUnmaintained; no Tailwind v4 story worth documenting.
React NativeNot targetedThe tokens are portable; nothing else is.
This table has not been generated. It is produced by scripts/build-reference.mts. Run pnpm run generate and reload.

Nothing on this site types a measured number by hand, so an ungenerated table shows this rather than an example.

Verify it worked

Four checks that between them catch almost every framework-level setup problem:

  1. A copied component renders styled. If it renders unstyled, Tailwind is not scanning the directory you copied it into. This is not an opsinjs problem and no opsinjs setting fixes it.
  2. A token resolves. Inspect any opsinjs element and confirm its colour comes from a custom property rather than a literal. If it is literal, the token layer is missing or imported in the wrong position.
  3. The server tree stays a server tree. Import a display-only component into a server component and confirm your framework does not complain. If it does, you have imported the interactive half.
  4. Dark mode does not flash. Toggle the theme class and confirm nothing re-renders. Theming is CSS; if something re-renders, a provider has crept in.

Troubleshooting

useState / createContext errors in a server component. You imported an interactive component into a server tree. Split the import, or mark the leaf as a client component rather than the whole page. Marking the whole page is the reflex fix and the expensive one.

Styles work in development and vanish in production. Almost always a Tailwind source-scanning difference between the two builds. Check your @source lines against Monorepo.

Hydration mismatch on a time or a formatted number. Time zone or locale differs between server and client. RelativeTime and Value document the hydration-safe contract; this is the one class of mismatch the components take responsibility for.

Tailwind v3. Upgrade first. There is no v3 compatibility layer and there will not be one.

Your framework is not listed. If it runs React 19 and Tailwind v4 and lets you control stylesheet order, it will most likely work. Tell us and we will add it to the matrix once there is something to test.

Next

  • Installation is where you pick the path that matches your setup.
  • Browser support is the runtime half of the support question.
  • Tailwind v4 covers @theme versus @theme inline, and the CSS order that breaks silently.

On this page