This guide is for developers, architects, and maintainers who want to understand how the React Bits project is built, how it works internally, how to run and deploy it, and how to extend it. It covers the dual nature of the repository — a component registry and a documentation web app — along with the build pipeline, the four-variant component system, the registry generation that powers CLI installs, and operational concerns for self-hosting a fork.
Note
This is the internals guide. For how to consume React Bits components in your own app (install, props, variants), see the User Guide. The two documents are complementary: the User Guide treats React Bits as a product; this guide treats the repository as a system.
Table of Contents
- Executive Summary
- System Architecture
- Technology Stack
- Repository Structure
- The Four-Variant Component System
- The Registry: How CLI Installs Work
- Build & Development Workflow
- The Documentation Web App
- Configuration Reference
- Deployment
- Adding & Maintaining Components
- Security Analysis
- Code Quality & Patterns
- Operations & Troubleshooting
- Potential Improvements
- Quick Reference
1. Executive Summary
React Bits is a registry-distributed React component library with an integrated documentation site. One repository serves two purposes:
- A component registry — 130+ animated components, each authored in four variants (JavaScript/TypeScript × plain-CSS/Tailwind). A build step compiles these into a static registry under
public/r/that the shadcn and jsrepo CLIs consume to copy components into end-user projects. - A documentation single-page app — a Vite + React 19 site (deployed at reactbits.dev) that previews every component, exposes interactive prop controls, generates copy-ready code, and hosts creative tools.
| Attribute | Value |
|---|---|
| Project type | Registry-distributed component library + documentation SPA |
| Language | JavaScript / TypeScript (React 19) |
| Build tool | Vite 5 |
| Registry tooling | jsrepo + @jsrepo/shadcn |
| Deployment target | Cloudflare (static assets, SPA fallback) |
| Package status | Private ("private": true, "version": "0.0.0") — not published to npm |
| License | MIT + Commons Clause |
| Maintainer | David Haz (DavidHDev) |
Important
The repository is not an installable npm package. Its
package.jsonisprivatewith version0.0.0. The distributable output is the generated registry (public/r/), not a bundle. This is the defining architectural decision: components are delivered as source, copied into consumer projects, never imported as a dependency.
2. System Architecture
There are two distinct data flows: the authoring/registry flow (how components reach end users) and the site flow (how the docs SPA renders).
┌──────────────────────────────────────────────────────────────────────┐
│ AUTHORING / REGISTRY FLOW │
│ │
│ src/content/ (JS-CSS source — canonical authoring location) │
│ src/tailwind/ (JS-TW source) │
│ src/ts-default/ (TS-CSS source) │
│ src/ts-tailwind/ (TS-TW source) │
│ │ │
│ │ jsrepo build (reads jsrepo.config.ts + componentMetadata) │
│ ▼ │
│ public/r/*.json (generated registry: one entry per variant) │
│ │ │
│ │ served at https://reactbits.dev/r/<Name>-<VARIANT> │
│ ▼ │
│ shadcn / jsrepo CLI ──copies source──▶ end-user project │
└──────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ DOCS SITE FLOW (Vite SPA) │
│ │
│ main.jsx ─▶ <Provider> ─▶ <App> (react-router-dom) │
│ │ │
│ ├─ / LandingPage │
│ ├─ /showcase ShowcasePage │
│ ├─ /sponsors SponsorsPage │
│ ├─ /tools/:toolId? ToolsPage (Background Studio, …) │
│ ├─ /:category/:subcategory CategoryPage (component demos) │
│ └─ /favorites FavoritesPage │
│ │
│ CategoryPage lazy-loads demos from src/demo/<Category>/<Name>Demo │
│ Demos render the live component + interactive prop controls │
│ Prop state is synced to the URL via nuqs (shareable preview links) │
└──────────────────────────────────────────────────────────────────────┘
Key insight: src/content/ (and its three sibling variant trees) is both the authoring source for the registry and the source the demos render. There is no duplication between "library code" and "site code" — the site renders the very components it distributes.
3. Technology Stack
Core
| Dependency | Version | Role |
|---|---|---|
react / react-dom |
^19.0.0 |
UI runtime |
vite |
^5.3.4 |
Dev server + bundler |
@vitejs/plugin-react |
^4.3.4 |
React fast-refresh / JSX |
react-router-dom |
^6.30.1 |
SPA routing |
typescript |
^5.7.3 |
Types for TS variants + config |
Registry tooling
| Dependency | Version | Role |
|---|---|---|
jsrepo |
^3.2.0 |
Registry build + CLI distribution |
@jsrepo/shadcn |
^2.0.0 |
Emits shadcn-compatible registry output |
Styling / UI
| Dependency | Version | Role |
|---|---|---|
@chakra-ui/react, @chakra-ui/icons |
^3.20.0 / ^2.2.4 |
Site UI components |
@emotion/react |
^11.14.0 |
Chakra styling engine |
tailwindcss, @tailwindcss/vite |
^4.0.3 |
Tailwind variants + site styling |
tailwind-merge, clsx, class-variance-authority |
— | Tailwind class composition |
geist |
^1.7.0 |
Font |
next-themes |
^0.4.6 |
Theme/color-mode handling |
lucide-react, react-icons |
— | Icon sets |
sonner |
^1.7.1 |
Toast notifications |
Animation / graphics (per-component dependencies)
| Dependency | Version | Used by |
|---|---|---|
gsap, @gsap/react |
^3.13.0 / ^2.1.2 |
Text animations, scroll triggers (e.g. SplitText) |
motion |
^12.23.12 |
Motion-driven components/landing |
three, @react-three/fiber, @react-three/drei, @react-three/postprocessing, @react-three/rapier |
— | 3D components/backgrounds (ModelViewer, Lanyard, Ballpit) |
ogl |
^1.0.11 |
Lightweight WebGL shaders (Aurora, PlasmaWave) |
postprocessing |
^6.36.0 |
Post-effects on backgrounds |
matter-js |
^0.20.0 |
Physics (Ballpit-style effects) |
gl-matrix, maath, mathjs, meshline |
— | Math/geometry helpers for shaders |
face-api.js |
^0.22.2 |
ReflectiveCard webcam features |
lenis |
^1.3.13 |
Smooth scrolling |
nuqs |
^2.8.6 |
URL-synced prop state for previews |
react-virtualized |
^9.22.6 |
Virtualized component index grid |
react-syntax-highlighter |
^15.6.1 |
Code blocks on the site |
react-confetti, react-haiku, @use-gesture/react |
— | Misc. interactions |
Dev tooling
eslint (^8.57.0) with eslint-plugin-react, eslint-plugin-react-hooks, eslint-plugin-react-refresh; prettier (^3.6.2); concurrently for parallel dev scripts; tw-animate-css; postcss-safe-parser.
Note
Most graphics dependencies are per-component, not site-wide for end users. The full set lives in this repo's
package.jsonbecause the site renders every component; a consumer only installs what their chosen components require (the registry declares each component's deps).
4. Repository Structure
react-bits/
├── index.html # Vite entry HTML
├── package.json # Private; scripts + full dependency set
├── vite.config.js # Vite config (aliases, Tailwind, PostCSS)
├── jsrepo.config.ts # Registry definition (THE registry source of truth)
├── tsconfig.json # TypeScript config
├── wrangler.jsonc # Cloudflare deployment config
├── README.md / CONTRIBUTING.md / LICENSE.md
├── scripts/
│ ├── generateComponent.js # Scaffolds a new component across all 4 variants
│ ├── generateLlmsText.js # Builds llms.txt for AI consumption
│ └── generateSitemap.js # Builds sitemap.xml
├── public/
│ ├── r/ # GENERATED registry (521 files) — jsrepo output
│ └── assets/ # Videos, images, fonts, demo assets
└── src/
├── main.jsx # React root (mounts <Provider><App/></Provider>)
├── App.jsx # Router + route table
├── content/ # JS-CSS component source (authoring + demos)
├── tailwind/ # JS-TW component source
├── ts-default/ # TS-CSS component source
├── ts-tailwind/ # TS-TW component source
├── demo/ # Per-component demo wrappers used on the site
├── docs/ # Get-Started docs as JSX (Introduction, Installation, McpServer)
├── pages/ # Route-level pages (Landing, Category, Showcase, …)
├── components/
│ ├── common/ # Site UI (ComponentList, BackToTopButton, Preview, …)
│ ├── landingnew/ # Landing page sections (Hero, Features, QuickStart, …)
│ ├── context/ # React context providers (Installation, ActiveRoute, …)
│ ├── layout/ # Providers, SidebarLayout
│ ├── navs/ code/ setup/ # Navigation, code display, Chakra provider setup
├── constants/
│ ├── Categories.js # Sidebar grouping of components
│ ├── Components.js # category → lazy demo import maps (componentMap)
│ ├── Information.js # componentMetadata (THE catalog) + VARIANTS
│ ├── code/ # Per-component code snippets shown on the site
│ ├── Tools.js Sponsors.js Showcase.js Site.js colors.js
├── hooks/ # useComponentProps, useInstallation, useStars, …
├── utils/ # cli.js, codeGeneration.js, favorites.js, fuzzy.js, …
├── tools/ # Background Studio, Shape Magic, Texture Lab
├── css/ # Site stylesheets
└── assets/ # Logos, icons, fonts
Tip
Two files are disproportionately important:
jsrepo.config.ts(defines what the registry exposes) andsrc/constants/Information.js(componentMetadata— the authoritative catalog). The registry config imports the catalog, so adding a component is fundamentally about adding a metadata entry plus the four source files.
5. The Four-Variant Component System
Every component exists in four physically separate source trees, keyed by language and styling:
| Variant | Source tree | File(s) |
|---|---|---|
JS-CSS |
src/content/<Category>/<Name>/ |
<Name>.jsx + <Name>.css |
JS-TW |
src/tailwind/<Category>/<Name>/ |
<Name>.jsx |
TS-CSS |
src/ts-default/<Category>/<Name>/ |
<Name>.tsx + <Name>.css |
TS-TW |
src/ts-tailwind/<Category>/<Name>/ |
<Name>.tsx |
<Category> is one of Animations, TextAnimations, Components, Backgrounds.
The Vite config exposes aliases for each tree:
// vite.config.js
resolve: {
alias: {
'@': '/src',
'@utils': 'src/utils',
'@content': 'src/content', // JS-CSS
'@tailwind': 'src/tailwind', // JS-TW
'@ts-default': 'src/ts-default', // TS-CSS
'@ts-tailwind': 'src/ts-tailwind', // TS-TW
'react-virtualized': 'react-virtualized/dist/commonjs',
}
}
Important
All four variants of a component must stay in sync. The contribution guide is explicit: "Each component change must be updated in all 4 variants of that particular component." A fix applied to only
src/content/will ship a divergent registry for the other three variants. This is the single biggest maintenance burden of the architecture.
6. The Registry: How CLI Installs Work
The registry is the distributable artifact. It is defined declaratively in jsrepo.config.ts and built into public/r/.
6.1 Registry definition
// jsrepo.config.ts (abridged)
import { defineConfig, type RegistryItem } from 'jsrepo';
import { output } from '@jsrepo/shadcn';
import { componentMetadata } from './src/constants/Information';
export default defineConfig({
registry: {
name: '@react-bits',
homepage: 'https://reactbits.dev',
repository: 'https://github.com/DavidHDev/react-bits',
excludeDeps: ['react'],
outputs: [output({ dir: 'public/r', format: true })],
items: [
...Object.values(componentMetadata).map(component =>
defineComponent({
title: component.name,
description: component.description,
category: component.category,
variants: component.variants,
})
)
].flat()
}
});
6.2 How variants become registry entries
defineComponent() expands each catalog entry into up to four RegistryItems, one per variant, each pointing at the correct source tree:
| Variant | Registry item name | Files included |
|---|---|---|
JS-CSS |
<Name>-JS-CSS |
src/content/<Category>/<Name> |
JS-TW |
<Name>-JS-TW |
src/tailwind/<Category>/<Name> |
TS-CSS |
<Name>-TS-CSS |
src/ts-default/<Category>/<Name> |
TS-TW |
<Name>-TS-TW |
src/ts-tailwind/<Category>/<Name> |
Defaults: if a catalog entry doesn't restrict variants, all four are generated (['JS-CSS','JS-TW','TS-CSS','TS-TW']).
Note
reactis excluded from declared dependencies (excludeDeps: ['react']) — consumers already have React, so the registry won't try to install it.
6.3 Special case: Lanyard
defineComponent() special-cases the Lanyard component:
dependencyResolution: 'manual'— its dependencies aren't auto-resolved.- It lists explicit files (the
.css/source) rather than a whole directory, because it bundles a 3D.glbasset that the generic directory-include would mishandle.
This is why Lanyard historically failed under shadcn add with a parse error on the binary asset (issue #960).
6.4 Output and serving
- The build writes one JSON file per registry item into
public/r/(~521 generated files in the current source). - After deployment, each is served at
https://reactbits.dev/r/<Name>-<VARIANT>(and.../r/<name>.jsonfor the shadcn registry shorthand). - Consumers fetch these via
shadcn add @react-bits/<Name>-<VARIANT>orjsrepo add https://reactbits.dev/r/<Name>-<VARIANT>.
Warning
public/r/is generated output, not hand-edited source. Never edit files underpublic/r/directly — they are overwritten byjsrepo build. Edit the component source trees and rebuild.
7. Build & Development Workflow
All workflows are driven by package.json scripts.
| Script | Command | Purpose |
|---|---|---|
dev |
concurrently … "npm run registry:dev" "vite" |
Run the registry watcher and the Vite dev server together. |
registry:dev |
jsrepo build --watch |
Rebuild the registry on source changes. |
registry:build |
jsrepo build |
One-off registry build → public/r/. |
build |
npm run registry:build && npm run llms:text && npm run sitemap && vite build |
Full production build. |
llms:text |
node ./scripts/generateLlmsText.js |
Generate llms.txt for AI consumption. |
sitemap |
node ./scripts/generateSitemap.js |
Generate the sitemap. |
new:component |
node scripts/generateComponent.js |
Scaffold a new component (all variants). |
lint |
eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0 |
Lint (zero-warning policy). |
format |
prettier --write . |
Format the codebase. |
7.1 Local development
npm install
npm run dev
This launches two processes concurrently (labeled registry and docs):
jsrepo build --watchkeepspublic/r/in sync as you edit components.viteserves the docs site with HMR.
7.2 Production build
npm run build
Build order matters: the registry is generated first, then llms.txt and the sitemap, then Vite bundles the site into dist/. The Vite step copies public/ (including the freshly built public/r/) into dist/, so the served site includes the registry.
Tip
Because
buildrunsregistry:buildfirst, a production build always ships a registry consistent with the current component source. If you only ranvite build, you could deploy a stale registry — always use the compositebuildscript for releases.
7.3 Build configuration notes
- PostCSS uses
postcss-safe-parser(vite.config.js) so malformed CSS in component files degrades gracefully rather than failing the build. .glbfiles are treated as assets (assetsInclude: ['**/*.glb']) for 3D components.- HMR is explicitly enabled in the dev server config.
8. The Documentation Web App
8.1 Entry & providers
src/main.jsx mounts the app inside a Chakra Provider, and src/App.jsx wraps everything in Router → NuqsAdapter → ActiveRouteProvider → Providers (context + Chakra theme), rendering an AnnouncementModal globally. On mount, App forces dark color mode (forceChakraDarkTheme()).
Note
main.jsxdeliberately pre-warmsreact-syntax-highlighterby rendering an empty<SyntaxHighlighter>into a detached root before mounting the app — a performance trick to load the highlighter chunk early so code blocks render instantly.
8.2 Routing
| Route | Page | Notes |
|---|---|---|
/ |
LandingPage |
Hero, Features, Testimonials, LiveDemo, QuickStart, Sponsors, CTA, Footer; shows a loader until fonts are ready. |
/showcase |
ShowcasePage |
Community projects using React Bits. |
/sponsors |
SponsorsPage |
Sponsor tiers (diamond/platinum/silver) + stats. |
/tools/:toolId? |
ToolsPage |
Lazy-loads Background Studio, Shape Magic, Texture Lab. |
/:category/:subcategory |
CategoryPage (in SidebarLayout) |
Renders a component's demo, or the Get-Started docs / Index. |
/favorites |
FavoritesPage (in SidebarLayout) |
User's saved components (localStorage). |
8.3 Component demos & the catalog
src/constants/Information.jsexportscomponentMetadata— the authoritative catalog mapping"<Category>/<Name>"→{ name, description, category, docsUrl, tags, … }. It also exportsVARIANTS = ['JS-CSS','JS-TW','TS-CSS','TS-TW'].src/constants/Components.jsexportscomponentMap— slug → lazyimport()of the demo insrc/demo/.CategoryPageuses this to code-split each demo.src/constants/Categories.jsdefines the sidebar grouping and theNEWhighlight list.
8.4 Interactive previews
The site's defining feature is live prop editing with shareable URLs:
useComponentProps(defaultProps)(insrc/hooks/) syncs each prop to the URL query string via nuqs, with type-aware parsers for numbers, booleans, and hex colors. Editing a prop updates the URL; sharing the URL reproduces the exact preview.src/utils/codeGeneration.jsturns the current prop values back into a JSX usage snippet (generatePropsString,injectPropsIntoUsage,createUsageExample), so the displayed code always matches the live preview.src/utils/cli.js(generateCliCommands) builds the shadcn/jsrepo/manual install commands for the selected language, style, and package manager.
8.5 Supporting features
- Search —
src/utils/fuzzy.jsimplements Levenshtein-based fuzzy matching for the component index. - Favorites —
src/utils/favorites.jspersists saved components inlocalStorageundersavedComponents, broadcasting afavorites:updatedevent for cross-tab/state sync. - Virtualized index —
ComponentListusesreact-virtualized(Grid,AutoSizer,WindowScroller) to render the large component grid efficiently. - Star count —
useStarsfetches and caches the GitHub star count inlocalStoragefor 24 hours, with a default fallback. - Installation preference —
useInstallation/InstallationContextremembers the user's preferred CLI tool and package manager.
9. Configuration Reference
9.1 vite.config.js
| Setting | Value | Purpose |
|---|---|---|
plugins |
react(), tailwindcss() |
React + Tailwind v4 |
server.hmr |
true |
Hot module replacement |
resolve.alias |
@, @utils, @content, @tailwind, @ts-default, @ts-tailwind, react-virtualized |
Import shortcuts per variant tree |
css.postcss.parser |
postcss-safe-parser |
Tolerant CSS parsing |
assetsInclude |
['**/*.glb'] |
Bundle 3D models as assets |
9.2 jsrepo.config.ts
| Setting | Value |
|---|---|
registry.name |
@react-bits |
registry.homepage |
https://reactbits.dev |
registry.repository |
https://github.com/DavidHDev/react-bits |
registry.excludeDeps |
['react'] |
registry.outputs |
output({ dir: 'public/r', format: true }) (shadcn output) |
registry.items |
Generated from componentMetadata via defineComponent() |
9.3 wrangler.jsonc (Cloudflare)
{
"name": "react-bits",
"compatibility_date": "2025-06-15",
"assets": {
"directory": "./dist",
"not_found_handling": "single-page-application"
}
}
| Setting | Value | Purpose |
|---|---|---|
assets.directory |
./dist |
Serve the Vite build output |
assets.not_found_handling |
single-page-application |
Route unknown paths to index.html (client-side routing) |
Note
There are no application secrets or environment variables required to build or run React Bits. It is a fully static site plus a static registry; it has no backend, database, or API keys. The only network calls are client-side (e.g. fetching the GitHub star count).
10. Deployment
React Bits deploys as a static site on Cloudflare.
10.1 Deploy steps
# 1. Build (registry → llms.txt → sitemap → vite build into ./dist)
npm run build
# 2. Deploy ./dist to Cloudflare (Wrangler)
npx wrangler deploy
The wrangler.jsonc config serves ./dist with SPA fallback, so deep links like /components/dock resolve to index.html and are handled by react-router-dom.
10.2 What gets served
- The bundled SPA (
index.html, JS/CSS chunks, assets). - The generated registry under
/r/(copied frompublic/r/intodist/r/), makinghttps://reactbits.dev/r/<Name>-<VARIANT>available to CLI consumers. llms.txtandsitemap.xmlfrom the generator scripts.
Important
The registry's availability depends on the site deployment. If the site is down,
shadcn add @react-bits/...and thehttps://reactbits.dev/r/...URL form will fail. Thejsrepo add github/davidhaz/react-bits ...form is resilient to site downtime because it pulls from GitHub directly.
10.3 Self-hosting a fork
To run your own instance:
- Fork and
npm install. - Update
jsrepo.config.ts(homepage,repository) and any hard-codedreactbits.devURLs (notably insrc/utils/cli.js, which builds install URLs againsthttps://reactbits.dev). npm run buildand deploy./distto any static host (Cloudflare, Netlify, Vercel static, S3+CloudFront). For non-Cloudflare hosts, configure SPA fallback toindex.html.
Caution
If you self-host but leave
src/utils/cli.js'sbaseUrl = 'https://reactbits.dev'unchanged, the install commands your site shows will point users back at the official registry, not your fork.
11. Adding & Maintaining Components
11.1 Scaffold a new component
npm run new:component <ComponentType> <ComponentName>
# e.g.
npm run new:component Backgrounds MyEffect
scripts/generateComponent.js creates empty files across every tree:
src/content/<Type>/<Name>/<Name>.jsx
src/content/<Type>/<Name>/<Name>.css
src/tailwind/<Type>/<Name>/<Name>.jsx
src/ts-default/<Type>/<Name>/<Name>.tsx
src/ts-default/<Type>/<Name>/<Name>.css
src/ts-tailwind/<Type>/<Name>/<Name>.tsx
src/demo/<Type>/<Name>Demo.jsx
src/constants/code/<Type>/<name>Code.js
11.2 Wire it up
- Fill in all four variant source files (keep them behaviorally identical).
- Write the demo in
src/demo/<Type>/<Name>Demo.jsx. - Add a metadata entry to
componentMetadatainsrc/constants/Information.js(name, description, category,docsUrl). - Register the demo import in
src/constants/Components.js(componentMap). - Add the display name to the right category in
src/constants/Categories.js(and toNEWif it's a fresh addition). - Run
npm run registry:buildand verify a<Name>-*entry appears underpublic/r/.
11.3 Contribution rules
From CONTRIBUTING.md:
- Branch naming:
feat/<feature-name>. - PRs target
main. - Every component change must be reflected in all four variants.
- Test locally on desktop and mobile; check the console for errors.
- New community components are not currently accepted — only enhancements and bug fixes to existing components.
Warning
The "all four variants" rule is enforced by review, not tooling. There is no automated check that the four variants of a component are equivalent — divergence is a real risk and a good candidate for future CI (see Potential Improvements).
12. Security Analysis
React Bits is a static, client-only project with a small attack surface, but there are points worth understanding — especially because its core model is shipping source code into other people's projects.
12.1 Trust model — code is copied, not sandboxed
The defining security consideration: components are copied as source into consumer projects and run with full application privileges. Consumers should review the source they install (which is the point of the model — full visibility), but they inherit whatever the component does.
| Area | Assessment |
|---|---|
| Supply chain | No npm package means no post-install scripts reach consumers; they get plain source files. The risk shifts to the registry serving the expected source — i.e. integrity of reactbits.dev. |
| WebGL / canvas | Components run shaders and animation loops with client resources. Malicious shaders aren't a concern in this first-party repo, but resource exhaustion (high CPU/GPU) is a real UX risk (issue #953). |
| Webcam access | ReflectiveCard uses face-api.js/webcam input. Consumers embedding it must obtain user consent and handle permissions; the effect requests camera access on the client. |
| External fetches | The site fetches the GitHub stars API client-side (utils.js). No credentials are sent. |
| Outbound links | Sponsor/showcase links use rel="noreferrer"/noopener (verified in SponsorsPage/ShowcasePage), mitigating reverse-tabnabbing. |
12.2 No secrets, no backend
There are no environment variables, API keys, or server-side code in the repository. There is nothing to leak in the build, and no authentication/authorization surface. This substantially reduces the security footprint.
12.3 Recommendations for consumers
Tip
Before deploying a React Bits component to production: (1) read the installed source — you own it; (2) sandbox or gate webcam/3D components behind explicit user action; (3) lazy-load and unmount heavy WebGL backgrounds; (4) pin the per-component dependency versions you install.
13. Code Quality & Patterns
13.1 Conventions
- Functional components + hooks throughout. No class components.
- Custom hooks isolate cross-cutting concerns —
useComponentProps,useInstallation,useStars,useTransition,useActiveRoute,useScrollToTop,useForceRerender. Context-backed hooks throw a clear error if used outside their provider (e.g. "useActiveRoute must be used within an ActiveRouteProvider"). - Lazy loading + code splitting — demos and tools are loaded with
React.lazy+Suspense, keeping the initial bundle small despite 130+ components. - URL as state — preview prop state lives in the URL (nuqs), making previews shareable and back/forward-navigable.
- Defensive
localStorageaccess —favorites.jsanduseStarswrap reads/writes intry/catchand validate shapes, tolerating disabled storage and corrupt values. - Zero-warning lint policy —
--max-warnings 0makes lint failures block.
13.2 Pattern: prop-driven, self-contained components
Each component is a single file (plus an optional .css) with a clear destructured prop signature and sensible defaults — e.g. SplitText exposes delay, duration, ease, splitType, from/to, threshold; Aurora exposes colorStops, amplitude, blend, speed. This consistency is what makes the registry/preview/codegen pipeline possible: the site can introspect defaults and generate accurate usage code.
13.3 Pattern: lifecycle-correct graphics
WebGL/canvas components consistently clean up on unmount — cancelling requestAnimationFrame, removing resize listeners, and releasing the GL context (e.g. Aurora calls WEBGL_lose_context on teardown; ClickSpark disconnects its ResizeObserver). This is essential given the "many components" use case and the WebGL context-limit risk.
13.4 Notable complexity hotspots
- The four-tree duplication is the dominant structural complexity — four copies of every component must be maintained in lockstep.
ToolsPagecarries significant inline UI logic (custom dropdown, tooltips, portals) that could be extracted.- Shader components embed GLSL as template strings (e.g.
Aurora), which is idiomatic forogl/threebut is opaque to JS tooling and linting.
14. Operations & Troubleshooting
Since React Bits is a static site, "operations" centers on the build and the registry's correctness.
Issue: Registry entry missing after adding a component
Cause: The component wasn't added to componentMetadata, so defineComponent() never generated its items.
Resolution: Add the metadata entry in src/constants/Information.js, then npm run registry:build. Confirm <Name>-JS-CSS, -JS-TW, -TS-CSS, -TS-TW appear in public/r/.
Issue: Variants are out of sync
Cause: A change was made to one tree (e.g. src/content/) but not the other three.
Resolution: Re-apply the change to src/tailwind/, src/ts-default/, and src/ts-tailwind/. There is no automated equivalence check — diff the four files manually.
Issue: jsrepo build produces stale or unexpected output
Cause: Editing files under public/r/ directly (they're generated), or a watcher not running during dev.
Resolution: Never edit public/r/. Use npm run dev (which runs registry:dev) or re-run npm run registry:build. Treat public/r/ as build output.
Issue: Lanyard (or other asset-bundling component) fails to install
Cause: Binary assets (.glb) mishandled by the generic installer (issue #960).
Resolution: Lanyard is special-cased in jsrepo.config.ts with dependencyResolution: 'manual' and explicit file lists. Ensure consumers use @latest CLI; recommend manual install for asset-heavy components.
Issue: Production deploy serves an old registry
Cause: Running vite build alone instead of the composite build script.
Resolution: Always run npm run build, which regenerates the registry before bundling.
Issue: Deep links 404 on a non-Cloudflare host
Cause: Missing SPA fallback. wrangler.jsonc handles this on Cloudflare via not_found_handling: single-page-application.
Resolution: Configure the host to rewrite unknown routes to /index.html.
15. Potential Improvements
These are observations for maintainers, derived from the codebase structure:
- Automate variant equivalence. The "all four variants in sync" rule is manual. A CI check that diffs the logical structure (or runs each variant's demo and snapshot-tests output) would catch divergence — the most common class of bug for this architecture.
- Generate variants from one source. The four trees are largely mechanical transforms (JS↔TS, CSS↔Tailwind). A codegen step from a single canonical source could eliminate the duplication burden entirely.
- Extract
ToolsPageUI. The custom dropdown/tooltip/portal logic is large and reusable; extracting it would slim a 780-line page. - Accessibility pass. Several interactive components are pointer-first (e.g.
Carouselkeyboard nav, issue #976). A systematic a11y audit + keyboard support would broaden adoption. - Dependency-version pinning guidance per component. TS type breakage from upstream graphics libs (e.g.
threeClock→Timer, issue #975) suggests the registry should record tested dependency version ranges. - Device-aware perf fallbacks. A shared helper to detect low-power devices and swap to static placeholders would operationalize the project's own performance advice.
16. Quick Reference
16.1 Scripts
npm run dev # registry watcher + Vite dev server (concurrently)
npm run registry:build # build registry → public/r/
npm run build # registry + llms.txt + sitemap + vite build → dist/
npm run new:component <Type> <Name> # scaffold all 4 variants
npm run lint # eslint, zero warnings allowed
npm run format # prettier
npx wrangler deploy # deploy ./dist to Cloudflare
16.2 Key files
| File | Role |
|---|---|
jsrepo.config.ts |
Registry definition (consumes componentMetadata) |
src/constants/Information.js |
componentMetadata — the authoritative catalog + VARIANTS |
src/constants/Components.js |
slug → lazy demo import map |
src/constants/Categories.js |
sidebar grouping + NEW list |
src/utils/cli.js |
builds shadcn/jsrepo/manual install commands |
src/hooks/useComponentProps.js |
URL-synced prop state (nuqs) |
vite.config.js |
aliases, Tailwind, PostCSS, asset handling |
wrangler.jsonc |
Cloudflare static + SPA fallback |
scripts/generateComponent.js |
new-component scaffolder |
16.3 Variant matrix
| Variant | Tree | Files | shadcn code | jsrepo code |
|---|---|---|---|---|
| JS-CSS | src/content |
.jsx + .css |
JS-CSS |
default |
| JS-TW | src/tailwind |
.jsx |
JS-TW |
tailwind |
| TS-CSS | src/ts-default |
.tsx + .css |
TS-CSS |
ts/default |
| TS-TW | src/ts-tailwind |
.tsx |
TS-TW |
ts/tailwind |
16.4 Facts at a glance
| Item | Value |
|---|---|
| Components | 130+ across 4 categories |
| Categories | Text Animations, Animations, Components, Backgrounds |
| Variants per component | 4 |
| Registry output | public/r/ (~521 generated files) |
| Registry name | @react-bits |
| Hosting | Cloudflare (./dist, SPA fallback) |
| Secrets / backend | None |
| License | MIT + Commons Clause |
| Ports | Vue (vue-bits.dev), Svelte (sveltebits.xyz) |