📚ReDocs
ReDocs

Component System

Component System allows creating reusable elements with unique IDs and client-side functionality through ComponentTemplate.

Component Features

Components are special NodeProxies that provide:

  • Unique instance IDs

  • Client-side integration

  • Props validation

  • TypeScript support

  • Scoped styling

Basic Component Example

1import { component } from "@reface/recast";
2
3const Button = component((props: ButtonProps, children: Children) => (
4 ​<button class="button" {...props}>
5 ​ ​{children}
6 ​</button>
7));
8
9// Usage
10Button({ class: "primary" })`Click me`; // Template Literal
11<Button class="primary">Click me</Button>; // JSX

Component Instance IDs

Each component instance gets unique ID for client-side integration:

1const Card = component((props: CardProps, children: Children) => (
2 ​<div __rcc={props.__rcc}>
3 ​ ​<h2>{props.title}</h2>
4 ​ ​<div class="content">{children}</div>
5 ​</div>
6));
7
8// Renders with unique ID:
9// <div __rcc="card-1">...</div>
10// <div __rcc="card-2">...</div>

Client Integration

Components can include scoped client-side code:

1const script = /*js*/ `
2 ​const componentId = document.currentScript.getAttribute('data-component');
3 ​const root = document.querySelector('[__rcc="' + componentId + '"]');
4 ​
5 ​// Component-scoped client code
6 ​root.querySelector('.button').addEventListener('click', () => {
7 ​ ​console.log('Clicked!');
8 ​});
9`;
10
11const Button = component((props: ButtonProps, children: Children) => (
12 ​<div>
13 ​ ​<button class="button">{children}</button>
14 ​ ​<script>{script}</script>
15 ​</div>
16));
17
18// Renders with linked IDs:
19// <div __rcc="button-1">
20// <button class="button">Click me</button>
21// <script data-component="button-1">...</script>
22// </div>

TypeScript Support

Components support full TypeScript typing:

1// Define props interface
2interface ButtonProps {
3 ​variant: "primary" | "secondary";
4 ​size?: "small" | "large";
5 ​disabled?: boolean;
6 ​onClick?: () => void;
7}
8
9// Create typed component
10const Button = component<ButtonProps>((props) => (
11 ​<button
12 ​ ​class={[
13 ​ ​ ​"button",
14 ​ ​ ​`button-${props.variant}`,
15 ​ ​ ​props.size && `button-${props.size}`,
16 ​ ​]}
17 ​ ​disabled={props.disabled}
18 ​ ​onClick={props.onClick}
19 ​/>
20));
21
22// Usage with type checking
23<Button
24 ​variant="primary" // ✓ required
25 ​size="small" // ✓ optional
26 ​disabled={true} // ✓ optional
27 ​invalid={true} // ✗ Error: unknown prop
28/>;

Functions vs Components

You can create reusable elements using both regular functions and components:

1// Regular function returns Template
2function button(props: ButtonProps) {
3 ​return div({ class: "button", ...props })`
4 ​ ​Click me
5 ​`;
6}
7
8// Component returns ComponentTemplate
9const Button = component(
10 ​(props: ButtonProps) =>
11 ​ ​div({ class: "button", ...props })`
12 ​ ​ ​Click me
13 ​ ​`
14);

Key Differences

  1. Instance Tracking

    • Functions: No instance tracking

    • Components: ✓ Unique ID for each instance

    1// Function - no ID
    2button({ onClick: () => {} });
    3// <div class="button">Click me</div>
    4
    5// Component - has ID
    6<Button onClick={() => {}} />;
    7// <div __rcc="button-1" class="button">Click me</div>
  2. Client Integration

    • Functions: Manual ID management needed

    • Components: ✓ Automatic ID linking

    1// Function - manual ID management
    2function card() {
    3 ​const id = generateId(); // Need custom solution
    4 ​return div({ id })`
    5 ​ ​<script>
    6 ​ ​ ​const root = document.getElementById('${id}');
    7 ​ ​</script>
    8 ​`;
    9}
    10
    11// Component - automatic ID management
    12const Card = component((props) => (
    13 ​<div>
    14 ​ ​<script data-component={props.__rcc}>
    15 ​ ​ ​const root = document.querySelector('[__rcc="${props.__rcc}"]');
    16 ​ ​</script>
    17 ​</div>
    18));
  3. Chainability and Reuse

    • Functions: New instance on each call

    • Components: ✓ Can be chained and reused like NodeProxies

    1// Function - creates new element each time
    2const base = button({ class: "primary" });
    3const withSize = button({ class: "primary", size: "large" }); // New instance
    4
    5// Component - can be chained like NodeProxies
    6const Base = <Button class="primary" />;
    7const WithSize = Base({ size: "large" }); // Chains from base
    8const WithText = Base`Click me`; // Chains from base
  4. Render Timing

    • Functions: Execute during template creation

    • Components: ✓ Execute during render phase

    1// Function - executes immediately
    2const result = button({ id: "btn-1" }); // Creates element now
    3
    4// Component - executes during render
    5const Button = component((props) => {
    6 ​console.log("Rendering button"); // Logs during render
    7 ​return button(props);
    8});
    9const result = <Button id="btn-1" />; // Just creates template

When to Use What

Use Functions when:

  • No client-side code needed

  • Simple reusable elements

  • No instance tracking required

  • Performance is critical

Use Components when:

  • Client-side integration needed

  • Instance tracking required

  • Props validation needed

  • Complex reusable elements

  • Working with other components

Best Practices

  1. Start with functions for simple elements

  2. Convert to components when needed:

    • Adding client code

    • Need instance tracking

  3. Use components for app architecture

  4. Use functions for utility elements