Building Custom React Frontends (FlowDot Apps)
Create custom React user interfaces for your workflows. Learn the App Editor, workflow linking, AI-assisted development, and display modes.
Building Custom React Frontends (FlowDot Apps)
FlowDot Apps let you create custom React interfaces for your workflows. Build interactive UIs that connect to your automations without deployment hassle.
What You'll Learn
- Create and edit FlowDot Apps
- Link workflows to apps
- Use the AI assistant for app development
- Configure display modes
- Understand the sandboxed environment
Part 1: Understanding FlowDot Apps
What Are Apps?
Apps are custom React frontends that:
- Run in a sandboxed browser environment
- Can invoke your workflows
- Use React 18 and Tailwind CSS
- Require no deployment or hosting
App vs Dashboard
| Dashboard | App |
|---|---|
| Auto-generated UI | Custom coded UI |
| Standard form layout | Any design you want |
| Limited customization | Full React power |
| Quick to use | Takes time to build |
Part 2: Creating Your First App
Step 1: Navigate to Apps
- Click Apps in the navigation
- Click My Apps
- Click Create New App
Step 2: Name Your App
- Enter a name (e.g., "Goat Frontend")
- Click Create
Step 3: The App Editor
You'll see:
- Code Editor (left): Write your React code
- Preview Button (top): Test your app
- Settings (hamburger): Configure options
- AI Assistant (right): Get help building
Part 3: App Code Basics
Environment Rules
FlowDot Apps run in a special environment:
| Rule | Example |
|---|---|
| No imports | React is global |
| No exports | Just define your function |
| Function naming | function MyApp() { } |
| Styling | Tailwind CSS only |
Starter Code
function GoatFrontend() {
const [count, setCount] = React.useState(0);
return (
<div className="p-4">
<h1 className="text-2xl font-bold">Hello FlowDot!</h1>
<button
onClick={() => setCount(count + 1)}
className="bg-primary-600 text-white px-4 py-2 rounded mt-4"
>
Count: {count}
</button>
</div>
);
}
Available Globals
React- Full React 18 APIReact.useState,React.useEffect, etc.invokeWorkflow(hash, inputs)- Run workflows- Tailwind CSS classes
- FlowDot color tokens (
primary-50toprimary-900)
Part 4: Linking Workflows
Why Link?
To call a workflow from your app, it must be linked first.
How to Link
- In the App Editor, click Link Workflows (sidebar)
- Browse your workflows
- Click a workflow to see its inputs/outputs
- Click Link
Viewing Linked Workflows
After linking:
- Workflow appears in "Linked Workflows" panel
- Click to see input/output schemas
- Use the hash in
invokeWorkflow()
Part 5: Invoking Workflows
Basic Invocation
function MyApp() {
const [result, setResult] = React.useState(null);
const [loading, setLoading] = React.useState(false);
const runWorkflow = async () => {
setLoading(true);
const response = await invokeWorkflow('aB3xY9kL2m', {
user_prompt: 'Do you prefer hay or grain?'
});
setResult(response);
setLoading(false);
};
return (
<div className="p-4">
<button
onClick={runWorkflow}
disabled={loading}
className="bg-primary-600 text-white px-4 py-2 rounded"
>
{loading ? 'Running...' : 'Ask the Goat'}
</button>
{result && (
<pre className="mt-4 p-4 bg-gray-100 rounded">
{JSON.stringify(result, null, 2)}
</pre>
)}
</div>
);
}
Input Mapping
The second argument maps to your workflow's inputs:
await invokeWorkflow('hash', {
user_prompt: userInput, // Maps to "User Prompt" input node
system_prompt: systemPrompt // Maps to "System Prompt" input node
});
Handling Results
const response = await invokeWorkflow('hash', inputs);
// Response structure
{
data: {
"node-id-1": {
nodeTitle: "LLM Query",
outputs: {
response: { value: "Baa! I love hay!" }
}
}
}
}
// Extract what you need
const llmOutput = Object.values(response.data)
.find(n => n.nodeTitle === 'LLM Query')
?.outputs?.response?.value;
Part 6: AI-Assisted Development
Using the AI Assistant
- Click the AI Assistant icon in the App Editor
- Select your model tier (Complex recommended)
- Describe what you want:
You: Build the most fun and cute goat simulator frontend.
What the AI Sees
The assistant has access to:
- Your current code
- Linked workflows and their schemas
- FlowDot platform context
- Available styling options
Iterative Development
You: Add a text input for the user's question.
[Apply changes]
You: Make the goat response appear in a speech bubble.
[Apply changes]
You: Add some cute goat emojis and animations.
[Apply changes]
Part 7: Display Modes
Available Modes
| Mode | Description | Use Case |
|---|---|---|
| Windowed | Standard view with FlowDot header | Default, branded |
| Fullscreen | Full viewport, minimal controls | Immersive apps |
| Embedded | No FlowDot UI at all | iframe embedding |
Setting Display Mode
- Click the hamburger menu in App Editor
- Find Display Mode
- Select your preference
- Save
Example: Fullscreen App
For a kiosk or dedicated app experience:
- Set Display Mode to Fullscreen
- Design edge-to-edge layout
- Include your own navigation if needed
Part 8: Complete App Example
function GoatSimulator() {
const [question, setQuestion] = React.useState('');
const [response, setResponse] = React.useState('');
const [loading, setLoading] = React.useState(false);
const askGoat = async () => {
if (!question.trim()) return;
setLoading(true);
setResponse('');
try {
const result = await invokeWorkflow('YOUR_WORKFLOW_HASH', {
user_prompt: question
});
// Extract the goat's response
const goatResponse = Object.values(result.data)
.find(n => n.nodeTitle === 'LLM Query')
?.outputs?.response?.value || 'The goat is speechless!';
setResponse(goatResponse);
} catch (error) {
setResponse('Baa! Something went wrong!');
}
setLoading(false);
};
return (
<div className="min-h-screen bg-gradient-to-b from-green-100 to-green-200 p-8">
<div className="max-w-2xl mx-auto">
<h1 className="text-4xl font-bold text-center text-green-800 mb-8">
Ask Gerald the Goat
</h1>
<div className="bg-white rounded-lg shadow-lg p-6">
<input
type="text"
value={question}
onChange={(e) => setQuestion(e.target.value)}
placeholder="Ask the goat anything..."
className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-green-500"
onKeyPress={(e) => e.key === 'Enter' && askGoat()}
/>
<button
onClick={askGoat}
disabled={loading}
className="w-full mt-4 bg-green-600 text-white py-3 rounded-lg hover:bg-green-700 disabled:opacity-50 transition"
>
{loading ? 'Thinking...' : 'Ask Gerald'}
</button>
</div>
{response && (
<div className="mt-6 bg-white rounded-lg shadow-lg p-6">
<div className="flex items-start gap-4">
<span className="text-4xl">goat emoji</span>
<div className="flex-1 bg-green-50 rounded-lg p-4">
<p className="text-green-800">{response}</p>
</div>
</div>
</div>
)}
</div>
</div>
);
}
Part 9: Publishing and Sharing
Making Your App Public
- Open App settings
- Set visibility to Public
- Save
Sharing Options
- Direct link: Share the app URL
- Embedded: Use iframe in other sites
- Community: Others can discover and clone
Summary
FlowDot Apps give you:
- Custom UIs for your workflows
- React + Tailwind in a sandboxed environment
- AI assistance for faster development
- Multiple display modes for different use cases
- No deployment - just build and share
Start simple, iterate with AI help, and create polished experiences for your workflows!
Congratulations - you've completed the FlowDot tutorials!