Claude Code for Gradio ML UI — Workflow Guide

Written by Michael Lip · Solo founder of Zovo · $400K+ on Upwork · 100% JSS Join 50+ builders · More at zovo.one

The Setup

You are building machine learning demo interfaces with Gradio, a Python library that creates web UIs for ML models with minimal code. Gradio provides pre-built input/output components (text, image, audio, video) and automatically generates a web interface from a Python function. Claude Code can create Gradio apps, but it over-engineers the UI with Flask/React instead of using Gradio’s declarative approach.

What Claude Code Gets Wrong By Default

  1. Builds a Flask API + React frontend. Claude creates a REST API with Flask and a React UI for the ML demo. Gradio replaces both — a single gr.Interface(fn=predict, inputs="text", outputs="text") creates the full web app.

  2. Writes custom file upload handling. Claude implements multipart form upload with validation. Gradio components like gr.Image(), gr.Audio(), and gr.File() handle upload, preprocessing, and type conversion automatically.

  3. Ignores Blocks for complex layouts. Claude stacks components linearly with gr.Interface. For complex UIs with tabs, columns, and conditional visibility, Gradio has gr.Blocks() — a more flexible layout system with gr.Row(), gr.Column(), and gr.Tab().

  4. Does not use Gradio’s sharing features. Claude deploys to a cloud server for sharing. Gradio has share=True that creates a public URL through a tunnel, and Hugging Face Spaces for permanent free hosting — no server deployment needed.

The CLAUDE.md Configuration

# Gradio ML Demo Project

## UI Framework
- Library: Gradio (Python ML web interfaces)
- Simple: gr.Interface for basic input/output
- Complex: gr.Blocks for custom layouts
- Hosting: Hugging Face Spaces or share=True

## Gradio Rules
- gr.Interface: fn, inputs, outputs for simple demos
- gr.Blocks: with gr.Blocks() as demo for complex UIs
- Components: gr.Textbox, gr.Image, gr.Audio, gr.Slider, etc.
- Events: component.change(), button.click() for interactivity
- Preprocessing: Gradio converts uploads to numpy/PIL/etc.
- State: gr.State for session-persistent data
- Queue: demo.queue() for concurrent request handling

## Conventions
- Keep predict function pure — Gradio handles I/O
- Use gr.Blocks for anything beyond simple in/out
- gr.Examples for preloaded example inputs
- CSS: gr.Blocks(css="...") for custom styling
- Launch: demo.launch(server_name="0.0.0.0") for containers
- Queue for long-running inference
- Type hints on functions for better component inference

Workflow Example

You want to create a demo for an image classification model. Prompt Claude Code:

“Create a Gradio app for an image classification model. Use gr.Blocks with an image upload, a classify button, a label output showing top 5 predictions with confidence bars, and example images. Add a queue for handling multiple requests.”

Claude Code should create a gr.Blocks() layout with gr.Image(type="pil") for upload, gr.Button("Classify"), gr.Label(num_top_classes=5) for output, gr.Examples with sample images, connect the button click to the prediction function, and call demo.queue().launch().

Common Pitfalls

  1. Not using queue for ML inference. Claude launches without .queue(). ML inference is slow — without queuing, concurrent requests fail or timeout. Always add demo.queue() before .launch() for production demos.

  2. Loading model inside the prediction function. Claude loads the model on every request with model = load_model() inside the predict function. Load the model once at module level — Gradio reuses the function for every request.

  3. Missing Hugging Face Spaces configuration. Claude creates a Gradio app but does not include the requirements.txt or app.py naming expected by Spaces. Hugging Face Spaces expects app.py as the entry point and requirements.txt for dependencies.