Guides

Server and Client Components

Keep data access on the server while adding reusable UI and interactive controls.

Combine server-rendered UI with interactive Client Components without moving data access into the browser. The example pairs a reusable greeting with a counter, showing where each kind of component belongs.

Choose a component for the task

TaskUse
Read services and render dataA Page or EFFRONT.Component
Handle clicks, input, or local stateA Client Component
Submit data to the serverA Server Function

Keep data access on the server and add Client Components only where interaction is needed.

Reuse server-rendered UI

In src/entry.effront.tsx from Routes, define Welcome and replace HomePage:

// src/entry.effront.tsx: add before HomePage.
const Welcome = EFFRONT.Component.make({
  render: ({ name }: { readonly name: string }) => Effect.succeed(<p>Hello, {name}.</p>),
});

// Replace HomePage.
const HomePage = EFFRONT.Page.make({
  render: () => Effect.succeed(<Welcome name="Ada" />),
});

Open / to see Hello, Ada. For data-backed UI, read application services inside the Effect returned by render.

Add an interactive control

Create src/components/counter.tsx with "use client" as its first statement:

"use client";

import { useState } from "react";

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

Import it in src/entry.effront.tsx and replace HomePage again:

// src/entry.effront.tsx: add to the imports.
import { Counter } from "./components/counter";

// Replace HomePage.
const HomePage = EFFRONT.Page.make({
  render: () =>
    Effect.succeed(
      <>
        <Welcome name="Ada" />
        <Counter />
      </>,
    ),
});

Open / and click Count: 0 to increment the counter. The Welcome component is rendered on the server.

See React's "use client" reference for the boundary rules.