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

@lms5400/babel-plugin-rsx

v1.2.0

Published

Babel plugin for RSX Components

Readme

RSX — On‑Demand Rendered Components

RSX is a component model designed for real‑time and imperative workloads where React’s state, effects, and memoization layers become accidental complexity.

RSX = On‑Demand Rendered Components You decide when rendering happens.

RSX components look familiar to React developers, but behave very differently under the hood. They eliminate the need for hooks entirely—including useState, useEffect, useCallback, useMemo, and useRef—by making rendering an explicit operation instead of a side‑effect of state changes.


Why RSX Exists

React excels at declarative UI driven by application state. But many real‑time systems are not state‑driven UIs:

  • Timers, clocks, and stopwatches
  • Game loops and input polling
  • Media processing (audio/video analysis)
  • Hardware and device bridges
  • Animation engines and render pipelines
  • High‑frequency data streams

In these domains, React often forces developers into patterns like:

  • Deep useEffect chains
  • useCallback for “stability”
  • useMemo to fight re‑execution
  • useRef as escape hatches
  • Logic hidden inside custom hooks

The result is indirect control, harder reasoning, and fragile behavior.

RSX flips this model.

Basic Structure

export default function Example({ view, update, destroy, render, props }) {
  // Everything in this scope runs exactly once on
  // mount and persists for the duration of the component.

  // Initial props snapshot (mount only)
  const initialProps = props;

  // Persistent state
  let value = 0;

  function increment() {
    value++;
    render(); // explicit re-render
  }y

  view((props) => {
    // The render function
    return <button onClick={increment}>{value}</button>;
  });

  update((prevProps, nextProps) => {
    // runs when props change
  });

  destroy(() => {
    // runs once on unmount
  });
}

How to Setup

1. Install the Package

npm install @lms5400/babel-plugin-rsx

Note: This plugin requires @babel/core and @babel/preset-react as peer dependencies. Most React projects using Vite or Next.js already have these bundled internally. If you're using Webpack or a custom setup, check if they exist in your node_modules/. If not, install them:

npm install --save-dev @babel/core @babel/preset-react

2. Configure Babel

Add the plugin to your Babel configuration. Choose the setup that matches your bundler:

Option A: Vite (recommended)

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { rsxTypeStripPlugin, rsxVitePlugin } from "@lms5400/babel-plugin-rsx/vite";

export default defineConfig({
  resolve: {
    extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".rsx"],
  },
  plugins: [
    // Plugin order matters:
    rsxTypeStripPlugin(),  // 1. Strip TypeScript (optional - only if using TS in .rsx)
    rsxVitePlugin(),       // 2. RSX → React transformation
    react({                // 3. JSX → JS
      include: /\.(jsx|tsx)$/  // note: no need to include rsx because rsxVitePlugin already transforms JSX in .rsx files
    }),
  ],
});

Option B: Webpack (Recommended for Webpack users)

Use the dedicated RSX loader which handles TypeScript stripping and RSX transformation:

// webpack.config.js
module.exports = {
  resolve: {
    extensions: [".js", ".jsx", ".ts", ".tsx", ".rsx"],
  },
  module: {
    rules: [
      // RSX files - use dedicated loader
      {
        test: /\.rsx$/,
        exclude: /node_modules/,
        use: "@lms5400/babel-plugin-rsx/webpack-loader",
      },
      // Regular JS/TS files
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        use: "babel-loader",
      },
    ],
  },
};

Option C: Babel Config (works with any bundler)

Create or update your babel.config.js:

module.exports = {
  presets: [
    ["@babel/preset-react", { runtime: "automatic" }],
    "@babel/preset-typescript", // Required for TypeScript in .rsx files
  ],
  plugins: ["@lms5400/babel-plugin-rsx"],
};

Note: This approach requires @babel/preset-typescript to strip TypeScript before RSX transformation.

3. [Highly Recommended] – Configure ESLint for RSX Files

For RSX-specific linting and recommended VS Code lint configuration, use the official plugin:

https://github.com/LMS007/eslint-plugin-rsx

Files to edit/create:

  • eslint.config.js
  • vscode/settings.json

4. [Optional] - Add TypeScript Support

RSX fully supports TypeScript in .rsx files. Follow these steps to enable it:

Step 1: Add RSX types to tsconfig.json

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "types": ["@lms5400/babel-plugin-rsx/types"]
  },
  "include": ["src/**/*", "src/**/*.rsx"]  // Include .rsx files
}

This provides:

  • Type declarations for *.rsx imports (so TypeScript understands import X from "./X.rsx")
  • The Ctx type for typing RSX component parameters

Step 2: Configure VS Code

Create or update .vscode/settings.json to treat .rsx files as TypeScript React:

{
  "files.associations": {
    "*.rsx": "typescriptreact"
  }
}

This enables full IDE support: syntax highlighting, IntelliSense, error checking, and go-to-definition.

Step 3: Update ESLint for TypeScript (if using ESLint)

If you followed Step 3 above to configure ESLint with @lms5400/eslint-plugin-rsx, update your eslint.config.js to use the TypeScript parser for .rsx files:

import tseslint from "typescript-eslint";

export default [
  // ... other configs
  {
    files: ["**/*.rsx"],
    languageOptions: {
      parser: tseslint.parser,
      parserOptions: {
        ecmaFeatures: { jsx: true },
      },
    },
    // ... rules
  },
];

Create Your First RSX Component

Create a file with the .rsx extension:

Note: The RSX() wrapper is necessary when mixing TypeScript and RSX. See TS_Semantics.md for the full rationale and a nested components.

// Counter.rsx
import type { RSX } from "@lms5400/babel-plugin-rsx/types";

interface CounterProps {
  name: string;
}

export default function RSX<CounterProps>(Counter({ view, render }) {
  let count = 0;

  function increment() {
    count++;
    render();
  }

  view((props) => (
    <>
      <label>{props.name}</label>
      <button onClick={increment}>Count: {count}</button>
    </>
  ));
})

The RSX parameter uses the Ctx<P> generic type with the following structure:

  • props - your component's props (type P)
  • view(cb) - register view callback, receives (props: P) => ReactNode
  • update(cb) - register update callback, receives (prev: P, next: P) => void
  • render() - trigger a re-render
  • destroy(cb) - register cleanup callback

Use It in Your React App

import Counter from "./Counter.rsx";

function App() {
  return (
    <div>
      <h1>My App</h1>
      <Counter name="Count clicks" />
    </div>
  );
}

The Core Idea: Render On Demand

In RSX:

  • Rendering is explicit
  • Logic runs once, not on every re‑render
  • Local variables behave like real variables
  • You call render() only when output must change

There is no implicit reactivity.

If nothing meaningful changed, nothing renders.

This matches how real‑time systems already work.


Why RSX Needs No Hooks (Including memo)

Hooks exist to compensate for React’s re‑execution model:

| React Hook | Why It Exists | | ------------- | ----------------------------- | | useState | Triggers renders indirectly | | useEffect | Run code after render | | useCallback | Prevent identity churn | | useMemo | Prevent recomputation | | useRef | Persist values across renders |

RSX removes the root cause:

  • The component function does not re‑execute on updates
  • Variables persist naturally
  • Side‑effects are just normal code
  • All data can be mutated
  • Updates are intentional

Because nothing re‑runs implicitly:

  • memo is unnecessary
  • callback stability is irrelevant
  • dependency arrays disappear

Mutating data in RSX is safe because the framework does not rely on immutability or identity checks to decide when to update the UI. You explicitly control when rendering happens, so there’s no hidden scheduling, diffing, or replay that could be confused by in-place changes and therefore there is nothing to “optimize around.” or “safeguard.”


Why render() in RSX Is Faster

When you call render() in RSX, you skip the overhead that React incurs on every update:

| React (on every render) | RSX (on render()) | | -------------------------------------------- | ---------------------------------- | | Re‑executes entire component function | Only re‑runs the view() callback | | Runs all hooks sequentially | No hooks to run | | Compares dependency arrays (useMemo, etc.) | No dependency tracking | | Recreates closures and inline functions | Functions created once, persist | | Checks memo wrappers for prop changes | No memo wrappers needed | | Schedules effects, flushes effect cleanup | Side‑effects are imperative code | | Data needs to be copied for immutability | No need to copy, just mutate in place |

The Real Cost of Hooks

In React, even a "simple" component with a few hooks pays these costs every render:

function ReactTimer() {
  const [time, setTime] = useState(0);           // hook 1
  const intervalRef = useRef(null);              // hook 2
  const start = useCallback(() => { ... }, []);  // hook 3 + dep check
  const stop = useCallback(() => { ... }, []);   // hook 4 + dep check

  useEffect(() => { ... }, [time]);              // hook 5 + dep check + cleanup

  return <div>{time}</div>;
}

Every frame: 5 hook calls, 3 dependency array comparisons, potential effect scheduling.

RSX Equivalent

function RsxTimer({ view, render }) {
  let time = 0;
  let intervalId = null;

  function start() {
    intervalId = setInterval(() => {
      time++;
      render();
    }, 1000);
  }
  function stop() {
    clearInterval(intervalId);
  }

  view(() => <div>{time}</div>);
}

On render(): just the view() callback runs. No hook overhead. No comparisons.

The performance gap widens with:

  • High‑frequency updates (60fps animations, real‑time data)
  • Many instances (100+ timers, particles, list items)
  • Complex hook graphs (effects depending on effects)

What Types of React Components Are Perfect for RSX conversion?

RSX shines where imperative control beats declarative diffusion.

Ideal Use Cases

  • Timers & schedulers
  • Animation loops (requestAnimationFrame)
  • Gamepad, MIDI, HID, or sensor input
  • Audio / video analysis
  • Streaming or polling systems
  • Canvas / WebGL / WebGPU renderers
  • Electron IPC bridges
  • High‑frequency UI updates

If a component:

  • Does work continuously
  • Talks to hardware or external systems
  • Maintains internal mutable state
  • Uses many useRefs as escape hatches and useCallbacks for stabilization
  • Should not re‑run on every parent render
  • Has tangled or deeply nested useEffect chains

…it’s likely a strong RSX candidate.


Designed to Be Used With React

RSX is not a replacement for React.

It is meant to be sprinkled into existing JSX projects:

  • Use React for layouts, routing, forms, and data fetching
  • Use RSX for hot paths and real‑time subsystems
import ReactComponent from 'ReactComponent.tsx'
import RsxComponent from 'RsxComponent.rsx'
...
<div>
 <ReactComponent name={hello world}/>
 <RsxComponent name={hello world}/>
</div>

RSX components:

  • Mount inside normal React trees
  • Coexist with JSX components
  • Do not affect React’s mental model elsewhere

Works Natively with TypeScript

RSX supports TypeScript end‑to‑end:

  • Typed props
  • Typed local state
  • Typed helpers and APIs
  • Full IDE inference

Because RSX avoids hook indirection:

  • Types are flatter
  • Control flow is obvious
  • Fewer generics and wrapper types

The result is clearer typings with less ceremony.


Easier to Read, Easier for AI to Write

RSX code is:

  • Linear
  • Explicit
  • Single‑pass

There are no hidden lifecycles, no dependency arrays, and no hook rules.

This makes RSX:

  • Easier for humans to reason about
  • Far less error‑prone when generated by AI

AI systems struggle with:

  • Hook ordering rules
  • Dependency correctness
  • Memoization correctness
  • Effect timing

RSX removes these failure modes entirely.

What you see is what runs.


Mental Model Summary

| React | RSX | | ---------------------- | ------------------------ | | State‑driven | Event‑driven | | Implicit re‑execution | Explicit rendering | | Hooks manage lifetimes | Code manages itself | | Optimization via memo | No optimization required |


When to Avoid RSX

RSX is not a general replacement for React. Prefer JSX + hooks when:

  • The component is mostly declarative UI (forms, lists, layout, content)
  • UI is derived from app or server state
  • Updates are infrequent or user-driven
  • The component is meant to be highly composable or generic
  • React’s conventions and consistency are more important than control

Further Reading