Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ A collection of examples and demos for people building the web with Netlify
- MCP with Netlify serverless functions - [Site](https://mcp-example-serverless.netlify.app/), [Code](https://github.com/netlify/examples/tree/main/examples/mcp/serverless-mcp)
- MCP with Hono - [Site](https://mcp-example-hono.netlify.app/), [Code](https://github.com/netlify/examples/tree/main/examples/mcp/hono-mcp)
- MCP with Express - [Site](https://mcp-example-express.netlify.app/), [Code](https://github.com/netlify/examples/tree/main/examples/mcp/express-mcp)
- Generative UI with C1 by Thesys - [Site](https://mcp-example-express.netlify.app/), [Code](https://github.com/netlify/examples/tree/main/examples/generative-ui)

## Repo organization

Expand Down
6 changes: 6 additions & 0 deletions examples/generative-ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Dependencies
node_modules
dist

# Local Netlify folder
.netlify
111 changes: 111 additions & 0 deletions examples/generative-ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
[Deploy to Netlify]: https://app.netlify.com/start/deploy?repository=https://github.com/netlify/examples/&create_from_path=examples/generative-ui&utm_campaign=dx-examples

![Netlify Examples](https://github.com/netlify/examples/assets/5865/4145aa2f-b915-404f-af02-deacee24f7bf)

# Netlify Generative UI

A generative UI application powered by [Thesys C1](https://www.thesys.dev) and built with React, Vite, and Netlify Functions. This app demonstrates real-time streaming AI responses with dynamic UI generation.

[Demo](./genui.mp4)

## Features

- **Generative UI**: Dynamic UI components generated based on AI responses
- **Streaming Responses**: Real-time streaming of AI completions
- **React + Vite**: Modern React development with fast builds
- **Netlify Functions**: Serverless backend for API integration
- **Thesys C1 Integration**: Advanced generative UI capabilities

## Prerequisites

Before getting started, you'll need:

1. A [Thesys API key](https://console.thesys.dev/keys) - Sign up at Thesys Console
2. A Netlify account for deployment

## Environment Variables

Create a `.env` file in the root directory with:

```bash
THESYS_API_KEY=your_thesys_api_key_here
```

## Local Development

1. **Install dependencies:**
```bash
npm install
```

2. **Install Netlify CLI (if not already installed):**
```bash
npm install -g netlify-cli
```

3. **Start the development server:**
```bash
netlify dev
```

This will start both the Vite dev server and Netlify Functions locally.

4. **Open your browser:**
Navigate to `http://localhost:8888` to see the application.

## Deploy to Netlify

### Option 1: One-Click Deploy

[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)][Deploy to Netlify]

### Option 2: Manual Deploy

1. Deploy using Netlify CLI:
```bash
netlify deploy --prod
```

## Project Structure

```
├── src/
│ ├── components/ # React components
│ ├── helpers/ # API and utility functions
│ ├── hooks/ # Custom React hooks
│ ├── App.tsx # Main application component
│ ├── main.tsx # Application entry point
│ └── index.css # Global styles
├── netlify/
│ └── functions/ # Netlify Functions
│ └── ask.mts # AI chat endpoint
├── public/ # Static assets
├── netlify.toml # Netlify configuration
├── vite.config.ts # Vite configuration
└── package.json # Dependencies and scripts
```

## API Endpoints

- `POST /api/ask` - Streaming AI chat endpoint
- Body: `{ "prompt": string, "previousC1Response"?: string }`
- Returns: Server-sent events with streaming response

## Technologies Used

- **Frontend**: React 19, Vite, TailwindCSS
- **UI Components**: Thesys GenUI SDK, Crayon
- **Backend**: Netlify Functions
- **AI**: Thesys C1 API
- **Build**: TypeScript, Vite
- **Deployment**: Netlify

## Learn More

- [Thesys C1 Documentation](https://docs.thesys.dev)
- [Netlify Functions Documentation](https://docs.netlify.com/functions/overview/)
- [Vite Documentation](https://vitejs.dev/)

## More Examples

Explore other examples of using the Netlify platform and primitives in this [examples repo](https://github.com/netlify/examples).
Binary file added examples/generative-ui/genui.mp4
Binary file not shown.
21 changes: 21 additions & 0 deletions examples/generative-ui/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Netlify Generative UI</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"
rel="stylesheet">
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

</html>
11 changes: 11 additions & 0 deletions examples/generative-ui/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[build]
command = "npm run build"
publish = "dist"

[functions]
directory = "netlify/functions"

[[redirects]]
from = "/api/*"
to = "/.netlify/functions/:splat"
status = 200
85 changes: 85 additions & 0 deletions examples/generative-ui/netlify/functions/ask.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import type { Context, Config } from "@netlify/functions";
import OpenAI from "openai";
import { ChatCompletionMessageParam } from "openai/resources/chat/completions";
import { transformStream } from "@crayonai/stream";

const client = new OpenAI({
baseURL: "https://api.thesys.dev/v1/embed",
apiKey: process.env.THESYS_API_KEY,
});

export default async (req: Request, context: Context) => {
// Handle CORS preflight
if (req.method === "OPTIONS") {
return new Response(null, {
status: 200,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
},
});
}

if (req.method !== "POST") {
return new Response("Method Not Allowed", {
status: 405,
headers: {
"Access-Control-Allow-Origin": "*",
},
});
}

try {
const { prompt, previousC1Response } = await req.json() as {
prompt: string;
previousC1Response?: string;
};

const messages: ChatCompletionMessageParam[] = [];

if (previousC1Response) {
messages.push({
role: "assistant",
content: previousC1Response,
});
}

messages.push({
role: "user",
content: prompt,
});

const llmStream = await client.chat.completions.create({
model: "c1-exp/openai/gpt-5/v-20250831",
messages: [...messages],
stream: true,
});

const responseStream = transformStream(llmStream, (chunk) => {
return chunk.choices[0]?.delta?.content || "";
});

return new Response(responseStream as ReadableStream, {
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache, no-transform",
"Connection": "keep-alive",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type",
},
});
} catch (error) {
console.error("Error in ask function:", error);
return new Response(JSON.stringify({ error: "Internal Server Error" }), {
status: 500,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
});
}
};

// No custom config needed - Netlify will automatically route to /.netlify/functions/ask
Loading