Advanced FeaturesUI Components

UI Components

JOEL provides a React-like component system for building user interfaces.

Component Declaration

[Compiled]
[target wasm32]

import ui

component Counter() {
  signal count = 0
  
  view (
    <Panel>
      <H1>Count: {count}</H1>
      <Button onClick={() => count++}>Increment</Button>
    </Panel>
  )
}

Signals (Reactive State)

component Counter() {
  signal count = 0
  signal name = "JOEL"
  
  view (
    <div>
      <p>Hello, {name}!</p>
      <p>Count: {count}</p>
    </div>
  )
}

Component Props

component Greeting(name: str, age: i32) {
  view (
    <div>
      <h1>Hello, {name}!</h1>
      <p>You are {age} years old</p>
    </div>
  )
}

# Usage
<Greeting name="JOEL" age={24} />

Event Handlers

component Button() {
  signal clicked = false
  
  fn handle_click() {
    clicked = true
  }
  
  view (
    <Button onClick={handle_click}>
      {clicked ? "Clicked!" : "Click me"}
    </Button>
  )
}

Conditional Rendering

component UserProfile(user: Option[User]) {
  view (
    <div>
      {match user {
        Some(u) => <div>Welcome, {u.name}</div>,
        None => <div>Please log in</div>
      }}
    </div>
  )
}

Lists and Loops

component TodoList(items: list[str]) {
  view (
    <ul>
      {for item in items {
        <li>{item}</li>
      }}
    </ul>
  )
}

Examples

Counter Component

[Compiled]
[target wasm32]

import ui

component Counter() {
  signal count = 0
  
  view (
    <Panel>
      <H1>{count}</H1>
      <Button onClick={() => count++}>+</Button>
      <Button onClick={() => count--}>-</Button>
      <Button onClick={() => count = 0}>Reset</Button>
    </Panel>
  )
}

export default Counter

Form Component

component ContactForm() {
  signal name = ""
  signal email = ""
  signal message = ""
  
  fn handle_submit() {
    # Submit form data
    print("Submitting:", name, email, message)
  }
  
  view (
    <Form onSubmit={handle_submit}>
      <Input 
        value={name} 
        onChange={(e) => name = e.value}
        placeholder="Name"
      />
      <Input 
        value={email}
        onChange={(e) => email = e.value}
        placeholder="Email"
      />
      <Textarea
        value={message}
        onChange={(e) => message = e.value}
        placeholder="Message"
      />
      <Button type="submit">Send</Button>
    </Form>
  )
}

SSR/SSG Support

# Server-side rendering
component BlogPost(slug: str) {
  # Fetch data on server
  let post = await fetch_post(slug)
  
  view (
    <Article>
      <H1>{post.title}</H1>
      <Content>{post.body}</Content>
    </Article>
  )
}

Cross-Platform

JOEL UI components compile to:

  • Web: React/Next.js
  • Mobile: React Native (via WASM)
  • Desktop: Electron/Tauri

Next Steps