Skip to main content

Installation

Last updated:

Install the CSS-only Frutjam UI library with Tailwind CSS v4, UnoCSS, or no build step at all. 65+ accessible components, zero JavaScript, WCAG AA. Works with Django, HTMX, Laravel, React, and any stack.

Install

bash
npm i -D frutjam

Then pick the line below that matches your setup. Every one gives you the same components.

Your setupWhat to addShips
Tailwind CSS v4@plugin "frutjam";only what you use
UnoCSSpresetFrutjam()only what you use
Vite, Astro, Rails, plain CSSimport 'frutjam/css'all components, 30 KB gzipped
No build stepa <link> tagall components

Tailwind CSS v4

You need a working Tailwind CSS project, plus Node and npm. Frutjam requires Tailwind 4.

Add Frutjam to your input.css with the @plugin directive:

css
1
2
@import "tailwindcss";
@plugin "frutjam";

That is the whole setup. All 65+ components are available immediately, and Tailwind ships only the ones your markup uses.

UnoCSS

Frutjam ships a preset, so UnoCSS generates only the components you use, the same way the Tailwind plugin does.

js
1
2
3
4
5
6
7
// uno.config.js
import { defineConfig, presetWind4 } from 'unocss';
import { presetFrutjam } from 'frutjam/unocss';

export default defineConfig({
  presets: [presetWind4(), presetFrutjam()],
});

Every class works as it does under Tailwind, and so do variants:

html
1
2
3
<button class="btn btn-error btn-xl">Delete</button>
<button class="md:btn-error">Red from the medium breakpoint up</button>
<div class="hover:card-primary">Fills on hover</div>

Add presetAttributify() and the component name becomes an attribute, with each value treated as a variant of it:

html
1
2
<button btn="error xl">Delete</button>
<button btn="md:(error xl)">Red and large from the medium breakpoint up</button>

The second line needs transformerVariantGroup() for the bracket syntax. Note that an attribute like btn is not valid HTML, so if you validate your markup, use the class form or UnoCSS's un- prefix.

Options

OptionDefaultPurpose
preflighttrueEmits Frutjam's tokens, reset and themes. Set to false if you already import frutjam/css/base yourself.
prefix''Matches the Tailwind plugin's prefix, so 'fj' gives you fj-btn and fj-btn="error".
layer'frutjam'The cascade layer the components are emitted into. It sorts below utilities, so a utility always wins over a component whatever order the presets are in.

UnoCSS treats link as a variant, for the CSS :link pseudo-class, and its default separators include -. So link-2xl is read as "2xl, when unvisited" rather than as a class name, and nothing is generated. It affects only this component, and only the link-* modifiers.

Either tell UnoCSS to split on colons alone:

js
1
2
3
4
export default defineConfig({
  separators: [':'],
  presets: [presetWind4(), presetFrutjam()],
});

Or use a prefix, which puts something other than link at the front of the name:

js
presets: [presetWind4(), presetFrutjam({ prefix: 'fj' })],   // fj-link-2xl

Every other component is unaffected. link is the only one of the 57 whose name collides with a pseudo-class.

No utility engine

Frutjam is built with Tailwind, but the components compile to ordinary CSS that reads custom properties. If your project uses no utility engine at all, import the stylesheet instead of the plugin:

js
import 'frutjam/css';

That is the whole setup. Every component class works, themes switch with data-theme as usual, and nothing else is required. It is 30 KB gzipped and carries the base styles, both themes and all 65+ components.

Pull in only what you need instead:

js
1
2
3
4
import 'frutjam/css/base';              // tokens and reset, always needed
import 'frutjam/css/components/button';
import 'frutjam/css/components/card';
import 'frutjam/css/themes/darkberry';  // optional

Or with no build step at all:

html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/frutjam@2/dist/frutjam.components.css">
Installed as a Tailwind plugin, Frutjam ships only the components you use. Imported as a stylesheet it ships all of them, because there is no build step reading your markup.

No build step

Drop a tag in and start writing markup. Use this for a prototype or a page you do not build, not for production, since it ships every component.

html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/frutjam@2/dist/frutjam.components.css">

To experiment with Tailwind's utilities on the same page, add Tailwind's browser build alongside it:

html
1
2
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/frutjam@2/dist/frutjam.min.css">
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>

JavaScript and React: optional, not required

Frutjam is native-first and CSS-first. The vast majority of components: buttons, badges, cards, forms, alerts, and more: work with pure HTML and CSS, zero JavaScript needed. Native HTML elements like <dialog>, <details>, and <input type="checkbox"> handle interactivity where browsers already support it. All components are WCAG AA accessible out of the box and work with Django, HTMX, Laravel, React, Next.js, and any stack.

A small number of components have no native HTML equivalent (toast notifications, combobox filtering, carousel controls). For those cases only, Frutjam ships lightweight optional helpers.

Vanilla JS: frutjam/js

Framework-agnostic ES module helpers for the components that need them. Attach to any DOM element, no framework required.

js
1
2
3
4
5
6
import { createModal } from 'frutjam/js'

const modal = createModal(document.getElementById('myModal'))
modal.open()   // calls showModal()
modal.close()  // calls close()
modal.toggle()

React Hooks: frutjam/react

Thin React wrappers around the same native DOM calls. Each hook returns a ref you attach to the element and methods to control it, no extra dependencies beyond React itself.

js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import { useModal } from 'frutjam/react'

export default function App() {
  const modal = useModal()
  return (
    <>
      <button onClick={modal.open}>Open</button>
      <dialog ref={modal.ref} className="modal">...</dialog>
    </>
  )
}

Available for: useModal, useDrawer, useCollapsible, useTooltip, useTabs, useToast, useCarousel, useCombobox.

Next Steps

Once installed, try the button component to see Frutjam in action, or explore the full component library.

AI Editor Integration

Using Claude Code, Cursor, or another AI editor?

Cherry MCP brings Frutjam's full component library directly into your AI editor's context, accurate class names, live docs, no copy-pasting.

Learn about Cherry MCP →

Starter project

Building with Django?

A working Django 6 project with Frutjam already wired up, accounts, a dashboard, themes and a markdown-backed CRUD example. Clone it and start building.

Get the Django starter →

Want to understand the thinking behind Frutjam's CSS-first approach? Read why we built it this way →

Found this useful?

Star Frutjam on GitHub. It's how other developers discover it.