This guide assumes you have read and understood Managing State, Handling Actions and Working with Forms.

Overview

A multi-step flow, or “chained action”, is the pattern of using the result of one user action to generate a new UI, which in turn contains new actions for the user to take. This is how you move from single interactions to complete, dynamic applications. In this tutorial, we will build a simple company research tool. The initial UI will show company details and two buttons: “View Products” and “View Locations.” Clicking a button will replace the current UI with the requested information, demonstrating a complete generative loop. You can checkout the full implementation here.

The Core Pattern: The Generative Loop

The entire concept of a multi-step flow is built on the generative loop we introduced in the “Handling Actions” guide. This loop can be repeated indefinitely: User Action → New Prompt → New UI.

Implementation

Let’s build our company research tool.

Step 1: Generating the Initial UI

First, we need to instruct the LLM to generate our starting UI. We’ll create a backend endpoint that uses a system prompt to ask for company information and the action buttons.
# System Prompt
You are a business research assistant. For a given company, you will provide a brief overview.

At the end of your response, you MUST add two buttons: "Products" and "Locations".
When your frontend calls this endpoint with a prompt like “Tell me about Apple,” the C1 API will return a C1 DSL that renders the company overview and the two interactive buttons.

Step 2: Handling the Action on the Frontend

Next, we’ll set up our React component to handle the user clicking one of the buttons. The handleAction function is the core of the chain. It takes the llmFriendlyMessage from the action (e.g., “Show products for Apple”) and sends it back to your backend API. Your backend will call the C1 API with the new prompt along with the conversation history(optional), and the C1 API will return a new C1 response. New response is used to add into converstaion or replace the old response.
import { useState } from "react";
import { C1Component, ThemeProvider } from "@thesysai/genui-sdk";

function CompanyResearchTool() {
  const [c1Response, setC1Response] = useState<string>(/* initial UI with buttons */);
  const [isLoading, setIsLoading] = useState<boolean>(false);

  const handleAction = async (action) => {
    if (isLoading) {
      return;
    }

    setIsLoading(true);
    try {
      // 1. Send the new prompt from the action to the same backend endpoint
      const response = await fetch("/api/research", {
        method: "POST",
        body: JSON.stringify({ prompt: action.llmFriendlyMessage }),
      });

      const data = await response.text();

      // 2. Update the state, replacing the old UI with the new one
      setC1Response(data.content);

    } finally {
      setIsLoading(false);
    }
  };

  return (
    <ThemeProvider>
      <C1Component
        c1Response={c1Response}
        isStreaming={isLoading}
        onAction={handleAction}
      />
    </ThemeProvider>
  );
}
With this implementation, you have created a complete multi-step flow. This new UI could contain even more actions—like a “View Details” button for each product—allowing the loop to continue as deep as your application requires.