Installation
guideReactive CSS is a single script. Choose the full bundle, the compact one or a custom build, load it with defer, and it processes the document on its own. Sizes, the development build, npm and CDN status, and production notes.
On this page · 11 sections+−
Choose a distribution
All three load the same way and behave the same for the features they contain. Prebuilt profile bundles are not distributed.
Measured from the 0.9.0 build (minified, gzip level 9, brotli quality 11, in bytes):
Script tag
<link rel="stylesheet" href="style.css"><script src="reactive-css.min.js" defer></script>That is all: the script processes the document by itself once it runs. The file is in the package’s dist/ folder: npm and CDN below say how to get it.
Development build and diagnostics
reactive-css.jsis the same runtime, not minified. It logs a console warning with the full text of each problem.- The minified bundles (full, compact and custom) log one summary line instead, and report each problem as a stable code with its values, such as
RCSS-P001⟦:on()⟧⟦events⟧⟦events⟧.ReactiveCSS.diagnosticslists every one. - Decode a code offline with
npx -p @reactivecss/core reactive-css explain "<message>", which needs no esbuild, or switch toreactive-css.jswhile you debug. The codes and their texts are indist/diagnostics.json.
npm
npm is the package’s distribution channel:
npm install @reactivecss/coreTo build custom bundles with the CLI as well, install it as a dev dependency together with esbuild, as below.
npm install --save-dev @reactivecss/core@0.9.0 esbuild- The runtime needs nothing. Copy
node_modules/@reactivecss/core/dist/reactive-css.min.js(or the compact file) to the folder your site serves, and load it with a script tag. Or import it, as below. - esbuild is needed only to build custom bundles. It is an optional peer dependency, so package managers do not install it for you: add it next to the package, as above, when you use the CLI.
- Install scripts. Recent npm versions hold back esbuild’s install script until you approve it (
npm install-scripts approve esbuild). In the project’s own test, the CLI built bundles without it, because esbuild’s platform binary comes from an optional dependency. - Imports:
@reactivecss/core: the ES module API;@reactivecss/core/auto: the full bundle (dist/reactive-css.min.js), which starts itself;@reactivecss/core/bundle: the bundle builder API (Node only);@reactivecss/core/catalog.json: the feature catalog.
CDN
npm CDNs such as jsDelivr and unpkg serve the package’s published files directly. No CDN of the project’s own is planned for the first release.
<script defer src="https://cdn.jsdelivr.net/npm/@reactivecss/core@0.9.0/dist/reactive-css.min.js"></script><script defer src="https://unpkg.com/@reactivecss/core@0.9.0/dist/reactive-css.min.js"></script>For production:
- Pin an exact version. Never use
@latestor a version range: such a URL can start serving a new release without notice. - Rely on version-pinned caching. CDNs cache files of a pinned version for a long time, because they never change. Unpinned URLs are cached briefly and can change.
- Upgrade deliberately. Read the changelog, change the version in the URL, and test your pages, then check
ReactiveCSS.diagnostics. - Self-host for full control: the file is static and has no dependencies. Custom bundles are always self-hosted.
- Subresource Integrity. With a pinned URL you can add an
integrityhash computed from that exact published file, pluscrossorigin="anonymous". Compute it from the file you downloaded from that URL:openssl dgst -sha384 -binary reactive-css.min.js | openssl base64 -A, prefixed withsha384-.
ES module
For programmatic use, import the ESM build and start it yourself. With the package installed, a bundler resolves @reactivecss/core to dist/index.js; without one, import the file directly.
import { start, refresh, stop } from '@reactivecss/core';start();The ES module keeps the full diagnostic text, and its type declarations resolve with moduleResolution set to bundler, node16 or nodenext.
Which stylesheets are read
The browser drops syntax it does not understand, including selectors such as :on(click), so the library reads the source text: inline <style> elements, and same-origin <link rel="stylesheet"> files through fetch(), normally from the HTTP cache. Sheets without reactive syntax are skipped untouched.
- Cross-origin sheets are skipped unless the
<link>hascrossoriginand the server sends CORS headers. - Cross-origin sheets are untrusted: their states work, but event rules,
bind(),--attr-*,focus:and reactivecontentare ignored. Theircontentstays native. @imported sheets,style=""attributes and shadow DOM stylesheets are not processed. The native rules of an imported sheet still apply.
Minifiers
Keep empty rules in stylesheets with Reactive CSS. A modifier-only event rule such as a.confirm:on(click, prevent()) {} is empty on purpose, and minifiers that remove empty rules delete it: esbuild with minify or minifySyntax, csso and Lightning CSS. Whitespace-only minification (esbuild minifyWhitespace) keeps it. Rules with declarations survive every one of them. Details: Browser compatibility.
Content-Security-Policy
Reactive CSS needs no 'unsafe-inline', hash or nonce of its own. It rewrites existing stylesheets in place through the CSSOM and never injects <style> elements or style="" attributes. Linked stylesheets must be readable with fetch(), so connect-src must allow their URL; 'self' covers this.
Configuration
Zero configuration is the default. Set options before the library script runs.
<script>window.ReactiveCSSConfig = { trustInlineStyles: false };</script><script src="reactive-css.min.js" defer></script>Browser targets
The build targets Chrome and Edge 111+, Firefox 128+ and Safari 16.4+, because event rules need CSS.registerProperty(). These are targets, not verified versions. See Browser Compatibility for what has actually been tested.
First paint
Keep defer. The first paint can happen before the script runs, so reactive values appear one script execution later, and transitions on reactive properties can play once on load. state() initial values are a native var() fallback and render immediately.