You can find the complete source code for this guide in the c1-custom-components example repository.
You might want to swap out certain components, such as the composer or sidebar, with your own to better match your design system. This can be done by destructuring and manually composing the C1Chat component and plugging in your own components where needed. Follow these steps to get started:
1

Destructure the C1Chat component

If you’ve followed the Quickstart guide, you should have a C1Chat component in your page.tsx file. You will need to replace that component with its constituent components.First, import ThemeProvider from @thesysai/genui-sdk, ChatProvider from @crayonai/react-core, and the following components from @crayonai/react-ui/Shell. Then, use them in your JSX code as follows:
<ThemeProvider>
  <ChatProvider
    threadListManager={threadListManager}
    threadManager={threadManager}
  >
    <Container
      logoUrl={"https://www.thesys.dev/favicon.ico"}
      agentName="C1Chat"
    >
      <SidebarContainer>
        <SidebarHeader />
        <SidebarContent>
          <NewChatButton />
          <SidebarSeparator />
          <ThreadList />
        </SidebarContent>
      </SidebarContainer>
      <ThreadContainer>
        <MobileHeader />
        <ScrollArea>
          <Messages />
        </ScrollArea>
        <Composer />
      </ThreadContainer>
    </Container>
  </ChatProvider>
</ThemeProvider>
For detailed information on how to create the threadManager and threadListManager, refer to this guide.
Here is an illustration of the composed UI that shows which components refer to which parts of the UI:
Custom components illustration
2

Build your own custom component

Next, build the custom component you would like to plug into the UI. For this example, let’s assume you want to replace the composer with a custom one. You can write the custom composer as follows:
import { useThreadActions, useThreadState } from "@crayonai/react-core";
import { Button } from "@crayonai/react-ui";
import { CircleX, SendIcon } from "lucide-react";
import { useState } from "react";

export const CustomComposer = () => {
  const [message, setMessage] = useState("");
  const { isRunning } = useThreadState();
  const { onCancel, processMessage } = useThreadActions();

  const handleMessageButton = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    if (isRunning) {
      onCancel();
      return;
    }

    if (!message) return;

    processMessage({ role: "user", type: "prompt", message });
    setMessage("");
  };

  return (
    <div className="bg-opacity-5 bg-white rounded-2xl p-4 w-7/12 mx-auto">
      <form onSubmit={handleMessageButton} className="w-full flex items-center">
        <input
          className="w-full h-full bg-transparent outline-none"
          placeholder="Type your message here..."
          value={message}
          onChange={(e) => setMessage(e.target.value)}
        />
        <Button
          variant="primary"
          iconRight={isRunning ? <CircleX /> : <SendIcon />}
          type="submit"
        />
      </form>
    </div>
  );
};
3

Plug in your custom component

Finally, replace the Composer component with your own CustomComposer component:
<ThemeProvider>
  <ChatProvider
    threadListManager={threadListManager}
    threadManager={threadManager}
  >
    <Container
      logoUrl={"https://www.thesys.dev/favicon.ico"}
      agentName="C1Chat"
    >
      <SidebarContainer>
        <SidebarHeader />
        <SidebarContent>
          <NewChatButton />
          <SidebarSeparator />
          <ThreadList />
        </SidebarContent>
      </SidebarContainer>
      <ThreadContainer>
        <MobileHeader />
        <ScrollArea>
          <Messages />
        </ScrollArea>
        <CustomComposer />
      </ThreadContainer>
    </Container>
  </ChatProvider>
</ThemeProvider>
That’s it! You’ve now replaced the default composer with your own custom one. You can do the same for the sidebar or any other component that you wish to replace.