G
YouTube TikTok Games
Skip to content
rust-js-langPublic

About

Rust.js, the Rust to "readable" JavaScript compiler. Built on rustc, inspired by Scala.js & ReScript

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

6,603 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rust.js logo

Rust.js

Rust in. Readable JavaScript out.

Rust.js compiles Rust to readable JavaScript. Built on rustc and inspired by Scala.js and ReScript, it keeps Rust's type system, traits, ownership, borrow checking, and compiler diagnostics while replacing code generation with a JavaScript backend.

The goal is simple:

Write Rust. Get readable JavaScript that works with every tool you already use.

Read the guide: how to write a JavaScript app in Rust.js, as it is today, from a first Vite app to bindings, React and the semantics.

Read the guide: how to write a JavaScript app in rust-js, each example's Rust beside the JavaScript it becomes.

Why Rust to JavaScript?

Rust gives us a powerful way to describe programs and catch mistakes before they run. JavaScript gives us the runtime and ecosystem in which much of our software already lives: browsers, React, npm packages, and tools such as Vite and Bun.

We want to use them together. Write a React component in Rust, import a JavaScript package, call a browser API, and inspect the result in DevTools. Let Rust check the program and let JavaScript run it.

WebAssembly provides a way to run compiled Rust in the browser. Rust.js explores a different compilation target: JavaScript itself. Its output is ordinary ES modules and JSX that existing JavaScript applications can import, debug, and build with their usual tools.

The longer-term motivation is Rust across the application stack. A server can use native Rust while a frontend uses Rust.js, with shared types and data models describing the contract between them. Support for #[derive(Serialize, Deserialize)] is one step toward that: for supported types and #[serde(...)] attributes, generated code writes and reads JSON with serde_json's behavior, including its deserialization errors.

What that looks like

A React component written in Rust:

pub fn App() -> JSX::Element {
    let (count, set_count) = use_state(|| 0);
    jsx! {
        <button onClick={move |_| set_count.update(|count| count + 1)}>
            {"Count is "}{count}
        </button>
    }
}

becomes:

export function App() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount((count) => (count + 1) | 0)}>
      Count is {count}
    </button>
  );
}

Imports omitted; see the React guide for a complete example.

The component remains a function, state remains a React hook, and the event handler remains an arrow function. The | 0 keeps the addition within a signed 32-bit integer, following Rust.js's release-style integer arithmetic.

Philosophy

Keep Rust's compiler in charge

rustc handles parsing, macro expansion, name resolution, types, traits, ownership, lifetimes, and borrow checking. Rust.js uses its checked program representation to generate JavaScript.

A program must pass Rust's checks before Rust.js emits output. Passing those checks is necessary, but the program must also use constructs that Rust.js supports. Unsupported constructs produce compiler errors.

Generated code is part of the product

You should be able to open the generated file, understand it, debug it, and call it from JavaScript. Names, module structure, formatting, and source maps all matter because people will work with this code.

That starts with representations a JavaScript programmer would recognize:

Rust JavaScript
struct object
tuple array
closure arrow function
Vec<T> Array
HashMap<K, V> Map, for supported key types
HashSet<T> Set, for supported element types
Option<T> usually a value or undefined; generic code boxes ambiguous values
async / .await async / await, with promises
module ES module

Runtime helpers are imported where the required behavior needs them, from @rust-js/std, released with the compiler, as ReScript's are from @rescript/runtime: import { $index } from "@rust-js/std";. Each helper should earn its place; readable output is a design constraint throughout the compiler. What std does that reaches no memory, a Peekable's next, is std's own body, compiled from the MIR the toolchain ships into the same package, imported the same way (ADR 0561).

Work with JavaScript's runtime

Rust and JavaScript have different runtime models. Reproducing every detail of native Rust would require machinery that can obscure the generated program. Rust.js favors JavaScript's own building blocks and makes the resulting semantic choices explicit.

For example, calling an async function starts its work immediately, up to the first await, just as it does in JavaScript. Native Rust futures wait until they are polled. This choice lets JavaScript callers receive an ordinary promise and lets the JavaScript event loop run the work without a Rust executor. It also means a future that is never awaited can still have effects.

These differences belong in the design, documentation, and tests. How Rust behaves in Rust.js lists each one, as it is today, and the design decisions record their reasons and costs. When a feature has no supported translation, compilation fails clearly.

Make interop fundamental

Importing JavaScript packages, exporting functions, using DOM APIs, and working with existing bundlers are core concerns. A Rust component should fit into a React application, and its output should remain useful to someone writing JavaScript.

Test the promise in real programs

Where behavior should match native Rust, run the same program through both implementations and compare the results. Snapshot the generated JavaScript so changes to its readability can be reviewed too.

Real applications test how those pieces fit together. The playground is written in Rust, compiled by Rust.js, and built with React and Vite. Developing it exercises the same compiler, bindings, and tooling that other applications use.

What works today

Rust.js supports a growing subset of Rust aimed at JavaScript applications:

  • structs, tuples, enums, pattern matching, and methods
  • traits and generics within the supported subset
  • closures, iterators, Option, Result, and common collection operations
  • strings, formatting, and async / await
  • ES modules, JavaScript imports, and DOM bindings generated from WebIDL
  • React, JSX, Vite, Fast Refresh, and source maps
  • serde-compatible JSON writing and reading, serde_json::Value and json! included (see the design decisions)

Support is specific to each feature. For example, a HashMap iterates in insertion order, and std::thread::spawn and a u128 in JSON are refused. How Rust behaves in Rust.js says what each part of Rust does today and how it differs from native Rust, and the known limitations what's refused.

Get started

Install Bun and Rust, then run these commands from the repository root. The repository pins the required Rust release and components.

bun run setup
bun run build
bun run react-example                 # React + Vite at localhost:5173
cd examples/vite-react && bunx rust-js build   # in another terminal, after you edit src/App.rs

An app's Rust is compiled one way: rust-js build, run in the app beside its own dev server, next dev or vite, which writes each module's JS and .d.ts beside its Rust (ADR 0412).

Testing

Run bun run typecheck for strict TypeScript checking with the pinned TypeScript 7 compiler. This checks scripts, tests, generators, and browser code without building Rust. Bun runs TypeScript directly but does not type-check it. The manual Check workflow also runs this command. JavaScript dependencies and generated JavaScript are not subject to strict source checking.

Run bun run test for native Rust comparisons, output snapshots, compiler diagnostics, source maps, React, browser, and Vite integration tests, the files side by side (ADR 0104), or bun run test:serial one after another. Run bun run fmt to format Rust and playground JSX, or bun run fmt:check to check. Run bun run to list all tasks.

Explore

Roadmap · Design decisions · Examples · React + Vite · DOM bindings · Browser tests · Compiler in WebAssembly

About

Rust.js, the Rust to "readable" JavaScript compiler. Built on rustc, inspired by Scala.js & ReScript

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages