npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cmj/juice

v0.9.8

Published

Zero-bloat React 19 RSC framework — streaming SSR, server actions, zero config.

Readme

🧃 Juice

Zero-bloat React 19 RSC framework.
Streaming SSR. Server Actions. Zero config.

Install

npm install @cmj/juice react@^19 react-dom@^19 vite@^6

Quickstart

npx @cmj/juice create my-app
cd my-app
npm install
npm run dev

Or scaffold for a specific target:

npx @cmj/juice create my-app --target cloudflare

Targets: bun (default), node, cloudflare, deno

CLI

| Command | Description | |---------|-------------| | juice create <name> | Scaffold a new Juice app | | juice dev | Start the Vite dev server | | juice build | Production build (client + SSR) |

API

juice/plugin

The Vite plugin. Zero config.

// vite.config.ts
import { defineConfig } from 'vite';
import juice from '@cmj/juice/plugin';

export default defineConfig({
  plugins: [juice()],
});

juice/runtime

The WinterCG-compliant edge runtime.

// server.ts
import { createRouter } from '@cmj/juice/runtime';
import manifest from './dist/flight-manifest.json' with { type: 'json' };

const handler = createRouter(manifest);
// handler: (Request) => Promise<Response>

Server Actions (Two-Argument Pattern)

Simple form actions receive FormData as the first argument — React 19 native. When you need more power, use ActionContext as the opt-in second argument:

'use server';
import type { ActionContext } from '@cmj/juice/runtime';

// Simple: FormData-only (React 19 native)
export async function addToCart(formData: FormData) {
  const id = formData.get('productId');
}

// Power: headers, cookies, params via second arg
export async function processWebhook(body: unknown, ctx: ActionContext) {
  ctx.request.headers.get('x-signature');
  ctx.cookies.get('session_id');
  ctx.params.id;
}

Test your actions with the public createActionContext factory:

import { createActionContext } from '@cmj/juice/runtime';
const ctx = createActionContext(new Request('https://example.com'));

How It Works

Source (.tsx) → Vite Plugin → flight-manifest.json → Runtime → Response
  1. Compile: The Vite plugin detects 'use client' and 'use server' directives, generates proxy modules, and emits a flight-manifest.json.
  2. Serve: The runtime reads the manifest and streams RSC responses using React 19's renderToReadableStream.
  3. Deploy: The Request → Response signature works on any WinterCG platform.

Client-Side Navigation

import { Link } from '@cmj/juice/client';

<Link href="/about">About</Link>
<Link href="/products" prefetch="viewport">Products</Link>
<Link href="/blog" activeClassName="active">Blog</Link>

Programmatic navigation:

'use client';
import { useRouter } from '@cmj/juice/client';

const router = useRouter();
router.push('/dashboard');
router.prefetch('/settings');

In dev mode, the Vite plugin auto-injects:

  • Client hydrationhydrateRoot() bootstrap so useState, onClick, useEffect work during development
  • HMR auto-refresh — Editing routes, components, or CSS triggers instant browser updates
  • SPA navigation — Navigation API + View Transitions for smooth page transitions, zero config

CSS & Styling

Import .css files from any component — they're collected and injected as <link> tags automatically.

// app/routes/layout.tsx
import './global.css';

CSS modules work out of the box:

import styles from './Button.module.css';
<button className={styles.root}>Click</button>

| Environment | Mechanism | |---|---| | Dev | Vite module graph walked recursively — <link> tags in <head> | | Production | CSS assets tracked in flight-manifest.json under the css key |

PostCSS, Sass, Less, Stylus — anything Vite supports works automatically.

Security

Juice ships secure defaults out of the box.

CSRF Protection

All mutating requests (POST, PUT, PATCH, DELETE) validate the Origin header against the Host header. Requests without a matching origin are rejected with 403 Forbidden.

// Enabled by default — no action needed.
const handler = createRouter(manifest);

// Allow specific cross-origin callers:
const handler = createRouter(manifest, {
  csrfProtection: {
    allowedOrigins: ['https://admin.example.com'],
  },
});

// Disable entirely (not recommended):
const handler = createRouter(manifest, { csrfProtection: false });

Client Component Isolation

'use client' modules that import Node.js builtins (fs, child_process, net, etc.) fail at build time:

JuiceClientBoundaryError: components/Upload.tsx is a 'use client' module
but imports Node.js builtin "fs". Move this logic to a server action.

Security Headers

  • Error/404 responses include Cache-Control: no-store and Content-Type: text/plain
  • HEAD requests unconditionally return empty bodies (RFC 9110 §9.3.2)
  • CSS bootstrap script injection mitigated via JSON.stringify escaping

Features

  • Security by default — CSRF protection, client boundary isolation, secure error headers
  • React 19 RSC — Server Components, Suspense, streaming SSR
  • Server Actions'use server' with FormData + opt-in ActionContext for headers, cookies, params
  • Client hydration in devuseState, onClick, useEffect work during development via hydrateRoot() bootstrap
  • HMR — Full hot module replacement — CSS instant, TSX full-reload
  • SPA navigation<Link> component + useRouter() hook + Navigation API bootstrap in dev
  • CSS pipelineimport './styles.css', CSS modules, PostCSS, Sass — dev and production
  • Zero config — One plugin call, auto-discovers routes from app/routes/
  • One dependency — Only urlpattern-polyfill for cross-platform routing
  • Multi-platform — Bun, Node.js, Cloudflare Workers, Deno
  • Empathic errors — "What-Why-How" error messages for fast debugging
  • Testable actions — Public createActionContext factory for unit testing
  • Dynamic CLI versioning — CLI template always uses the currently installed version

License

MIT