Code Export

The @nuforge/codegen package turns a program into a clean, standalone React component — with no nuforge at runtime. The result is plain .tsx you can drop into any React project.

import { toReact } from '@nuforge/codegen';

const tsx = toReact(nu.state.program); // a .tsx component string

What it maps

  • A component becomes function Name(props: Props) { ... }, plus a Props interface generated from its props.
  • A val state becomes const [x, setX] = useState(init); an assignment x = ... becomes setX(...).
  • @if={c} becomes {c && (<jsx/>)}.
  • @each={(item, i) in list} becomes {list.map((item, i) => <jsx key={i} />)}.
  • class and @classList become className={[...].filter(Boolean).join(' ')}.
  • A two-way binding becomes value={x} onChange={(e) => setX(e.target.value)}.
  • == and != become === and !==.
  • A val bound to an arrow function (val inc = () => count = count + 1;) becomes a plain callback — const inc = () => setCount(count + 1); — not useState, whether it's called inline ({f(3)}) or passed by reference (onClick={inc}).
  • A nested-state assignment (obj.a = obj.a + 1) clones through every ancestor level and calls the setter — setObj({ ...obj, a: obj.a + 1 }) — rather than mutating the state object in place.
  • The default <slot/> becomes {children}, and the component's props always destructures children when its template contains one (see Slots below).

Example

Input DSL:

component App() {
  val name = "nuforge";
} => (
  <div>
    <input value:={name} />
    <strong>{name}</strong>
  </div>
)

Generated output:

import { useState } from 'react';

export function App() {
  const [name, setName] = useState("nuforge");

  return (
    <div>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <strong>{name}</strong>
    </div>
  );
}

This powers an "export to real code" feature: a no-code builder can hand the user a real, framework-native component instead of locking them into a proprietary runtime. It is a key differentiator — the output owes nothing to nuforge once exported.

Next.js: 'use client' only when needed

The output follows the App Router convention. toReact adds the leading 'use client' directive only when the module actually needs the client — a component with state, an event handler, or a two-way binding. A purely presentational page stays a React Server Component (no directive, and no useState import):

// static page → Server Component
export function Card(props: CardProps) {
  const { title = 'Card' } = props;
  return <h3 className="card">{title}</h3>;
}
// has state → client module
'use client';
import { useState } from 'react';

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

Pass toReact(program, { useClient: true }) to force the directive, or imports: false to inline into an existing module.

External components & imports

If your page uses components from another package — e.g. an external <$Motion/> backed by motion/react, or an icon component — map their names so the export stays self-contained. Only names actually used (and not defined locally) are imported, grouped per module:

const tsx = toReact(program, {
  componentImports: {
    Motion: { from: 'motion/react', named: 'motion' }, // import { motion as Motion } from 'motion/react'
    Icon: 'lucide-react', // import { Icon } from 'lucide-react'
    Chart: { from: './chart', default: true }, // import Chart from './chart'
  },
});
'use client';
import { motion as Motion } from 'motion/react';

export function App() {
  return (
    <Motion animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.4 }}>
      Hello
    </Motion>
  );
}

This is what keeps an animated page (Motion / GSAP wrappers / design-system components) intact once exported.

Slots

A default (unnamed) <slot/> exports to React as {children} and to Vue as <slot /> — the component's generated props/interface always includes children, even when it declares no other props.

component Card() {} => (<div class={"card"}><slot /></div>)
interface CardProps {
  children?: any;
}

export function Card(props: CardProps) {
  const { children } = props;
  return (
    <div className="card">
      {children}
    </div>
  );
}

Named slots (<slot @name="header" />) export to Vue as <slot name="header" /> — Vue has native named-slot support. React does not have an equivalent mechanism yet: toReact throws a clear error naming the slot rather than silently emitting the same {children} for every named slot (which would lose the distinction between them and produce broken output). This is a known, deliberate limitation — build named-slot content as separate reusable components instead until React codegen supports it.

Other targets: HTML and Vue

Export is not limited to React.

toHtml(frame.view) renders an evaluated View tree to a static HTML string — no JavaScript, no nuforge runtime. Good for static export, email templates, and previews. Event handlers and external (host React) components are omitted by design — static HTML has no JavaScript to attach a handler to, or a React tree to mount an external component into. This isn't a gap to fix; if you need working handlers or external components, export to React instead.

import { Nu } from '@nuforge/core';
import { toHtml } from '@nuforge/codegen';

const nu = Nu.fromSource(SOURCE);
const frame = nu.createFrame({ component: { name: 'App' } });
const html = toHtml(frame.view); // a static HTML string

toVue(program, opts?) emits a Vue 3 Single-File Component: val becomes ref, @if/@each become v-if/v-for, two-way bindings become v-model, and events become @event.

import { toVue } from '@nuforge/codegen';

const sfc = toVue(program, { component: 'App' });
<script setup lang="ts">
import { ref } from 'vue';

const name = ref("nuforge");
</script>

<template>
  <input v-model="name" />
  <strong>{{ name }}</strong>
</template>

Reactive-props destructure in the generated SFC requires Vue 3.5+.

Next steps

  • React Rendering — render a Frame's reactive View tree live with React.
  • Editor SDK — build the visual editing experience around a program.