Your First Application

Now that you have survived the installation, it is time to actually build something. Cuek components are just functions that return a function. It is that simple. If you can handle a basic JavaScript closure, you can handle Cuek.

The Example

We are going to build a counter. It is not exactly original, but it perfectly illustrates how Cuek handles props, state, and updates without any framework magic.

index.html

You need a place for your app to live. A simple div with an id is all you need.

<!DOCTYPE html>
<html lang="en">
  <body>
    <div id="app"></div>
    <!-- Your bundled JavaScript entry point -->
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

Counter.tsx

This is your actual component. Notice the total lack of useState or complex hooks.

import { ComponentContext } from "cuekjs";

interface Props {
  title: string;
}

export default function Counter(props: Props, ctx: ComponentContext) {
  // This is your state. It is just a variable. No hooks. No proxies.
  let count = 0;

  const increment = () => {
    count++;
    // We tell the UI to update explicitly.
    ctx.rerender();
  };

  return () => (
    <div class="counter">
      <h2>{props.title}</h2>
      <p>Current count: {count}</p>
      <button onclick={increment}>Increment</button>
    </div>
  );
}

main.tsx

The entry point that ties it all together.

import { mount } from "cuekjs";
import Counter from "./Counter";

const root = document.getElementById("app");
if (root) {
  // Mount the component to the DOM.
  mount(root, <Counter title="Cuek Counter" />);
}

How it works

There are three things you need to understand here. They are simple but fundamental to how Cuek works.

  1. Props: They are just the first argument to your function. They do not magically trigger updates because nothing in Cuek is magical.
  2. State: It is literally just a variable inside your function’s scope. since your component returns a function (the render function), it forms a natural closure over that variable. It stays alive as long as the component is mounted.
  3. Rerendering: When you change your data, the DOM has no idea. You have to tell it by calling ctx.rerender(). This calls your render function again, compares the result to the previous one, and patches only the parts of the DOM that actually changed.

It is explicit. It is predictable. And it is fast because it does not waste time guesssing what you want.

The Destructuring Trap

If you are coming from React, you might be tempted to destructure your props at the top of your function:

// ❌ THIS WILL NOT UPDATE
function Display({ count }) {
  // You just copied the initial value into a local variable.
  // It will never change during a rerender.
  return () => <p>Count: {count}</p>;
}

Cuek components only run once when they mount. This creates a closure over the variables in that function. If you destructure a primitive like a number or a string, you are locking in that initial value forever.

When a component updates, Cuek mutates the underlying props object reference. To see those changes, your render function must read directly from that reference:

// ✅ DO THIS INSTEAD
function Display(props) {
  // We read from the props reference on every render
  return () => <p>Count: {props.count}</p>;
}

If you are absolutely desperate to destructure, you can do it inside the returned closure. We do not recommend it because it makes your render function look cluttered, but it technically works:

// ⚠️ IT WORKS, BUT WHY WOULD YOU?
function Display(props) {
  return () => {
    const { count } = props;
    return <p>Count: {count}</p>;
  };
}

Cuek does not use “magic proxies” to intercept your destructured variables. It is just plain JavaScript doing exactly what you told it to do.

A Feature or a Flaw?

Honestly, both. We openly admit that not being able to destructure primitive props is a Developer Experience (DX) weakness compared to heavier frameworks.

But this is the exact trade-off Cuek makes intentionally. We chose plain JavaScript predictability over compiler magic or reactive proxies. By forcing you to read directly from props, we guarantee your closures behave exactly like normal JavaScript closures always have.

No hidden mechanics. No surprises.