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 !==.

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.

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.

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.