Building Better Workflow UIs: From Collapsed Cards to Full Markdown Rendering
How we transformed our workflow execution interface by ditching collapsed cards for full markdown rendering, added export features, and integrated expert team creation into our prompt templates.
Building Better Workflow UIs: From Collapsed Cards to Full Markdown Rendering
When building workflow automation tools, one of the biggest challenges is presenting complex, multi-step outputs in a way that's both readable and actionable. Recently, I spent a development session completely overhauling our workflow execution UI, and the results were transformative.
The Problem: Information Hiding
Our original interface used collapsed prompt section cards to display workflow step outputs. While this seemed like a good idea for managing screen real estate, it created a frustrating user experience:
- Users had to constantly expand/collapse sections to read content
- The truncated previews were often misleading
- Context switching between sections broke the flow of understanding
- No easy way to export or work with the generated content
The Solution: Embrace Full Rendering
Instead of fighting against the natural length of AI-generated content, we decided to embrace it. Here's what we implemented:
1. Full Markdown Rendering
We completely removed the parsePromptSections() function and PromptSectionCard components, replacing them with full inline MarkdownRenderer for all completed step outputs:
// Before: Collapsed cards with truncated content
<PromptSectionCard
title={section.title}
content={truncate(section.content, 200)}
isExpanded={false}
/>
// After: Full markdown rendering
<MarkdownRenderer content={step.output} />
2. Action-Packed Toolbars
Each completed step now features a comprehensive action toolbar:
.mdDownload - Export content as markdown files- Copy - One-click clipboard copying
- Edit - Inline editing capabilities
- Retry - Re-run individual steps
The download functionality was particularly satisfying to implement:
const downloadMarkdown = (content: string, filename: string) => {
const blob = new Blob([content], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${sanitizeFilename(filename)}.md`;
a.click();
URL.revokeObjectURL(url);
};
3. Expert Team Integration
We enhanced our prompt templates with "Step 0: Assemble the Expert Team" sections. Now, every workflow automatically generates 3-5 domain experts matched to the project's tech stack:
## Step 0: Assemble the Expert Team
Based on the target repository's stack, assemble 3-5 domain experts:
- **Sarah Chen** (Full-Stack Developer) - React, Node.js, TypeScript
- **Marcus Rodriguez** (Database Architect) - PostgreSQL, Redis, data modeling
- **Alex Kim** (Frontend Specialist) - UI/UX, component libraries, accessibility
Lessons Learned: The Pain Points That Made Us Stronger
Every development session has its challenges. Here are the key lessons that emerged:
Authentication Environment Variables
The Issue: Spent time debugging JWT generation because I assumed the auth secret was stored in NEXTAUTH_SECRET.
The Reality: This project uses AUTH_SECRET, not NEXTAUTH_SECRET.
The Lesson: Always check the actual environment variable names in your project, don't assume standard conventions.
Workflow Execution Architecture
The Issue: Created workflows that stayed perpetually "running" with all steps "pending".
The Discovery: The start mutation only sets database status - actual execution requires consuming the Server-Sent Events (SSE) endpoint.
The Solution: After calling start, connect to /api/v1/events/workflows/[id] via fetch + ReadableStream to drive the AsyncGenerator execution.
// Don't just start the workflow
await workflowStart.mutateAsync({ id: workflowId });
// You need to consume the SSE endpoint too
const response = await fetch(`/api/v1/events/workflows/${workflowId}`);
const reader = response.body?.getReader();
// ... handle the stream to actually execute steps
Input Validation Gotchas
The Issue: Passing workflow input as a simple string caused Zod validation errors.
The Reality: The schema expects z.record(z.string()) - an object, not a string.
The Workaround: Wrap string inputs in an object: input: { text: "..." }
Development Environment Dependencies
The Issue: Tried running a Playwright screenshot script from /tmp/ and hit module resolution errors.
The Lesson: Node.js module resolution is relative to where you run the script, not where it's located. Keep automation scripts in your project root or handle dependencies explicitly.
The Results: A More Usable Interface
After implementing these changes, we tested everything end-to-end with a new "Expert Team Test" workflow. The results were immediately apparent:
- Better Readability - Full markdown rendering made complex outputs much easier to scan and understand
- Improved Workflow - Users could export individual steps as markdown files for documentation or further processing
- Enhanced Context - Expert team assignments gave every prompt template more personality and domain-specific guidance
- Reduced Friction - No more clicking through collapsed sections to find the information you need
What's Next?
While this session accomplished its core goals, it also revealed some areas for future improvement:
- Smart Navigation - For very long outputs (10k+ tokens), a table of contents or section navigation could help
- Cost Estimation - Update the
estimateWorkflowCostfunction to account for thegenerateCountmultiplier - Input Flexibility - Consider making the template resolver handle string inputs directly instead of requiring object wrapping
Key Takeaways
- Don't hide information - If your users need to see content, show it to them fully rather than forcing interaction to reveal it
- Make content actionable - Export and copy functionality turns your UI from a display into a productivity tool
- Test the full flow - Database mutations might succeed while the actual execution pipeline fails silently
- Environment setup matters - Small details like auth variable names and module resolution can derail debugging sessions
The best UIs get out of your users' way. By moving from collapsed, hidden content to full, actionable displays, we transformed our workflow interface from a barrier into a bridge - connecting users directly with the AI-generated content they need to move their projects forward.