{
  "@context": "https://schema.org",
  "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-02-18-one-shot-any-web-app-with-gradio-s-gr-html/#webpage",
  "@type": [
    "WebPage",
    "ArchiveComponent"
  ],
  "articleBody": "**Gradio 6** quietly shipped a very powerful feature: **`gr.HTML`** now supports custom templates, scoped CSS, and JavaScript interactivity. Which means you can build pretty much any web component — and Claude (or any other frontier LLM) can generate the whole thing in one shot: frontend, backend, and state management, all in a single Python file.\n\nWe tested this by building different types of apps. Each one is a single Python file, no build step, deployable to Hugging Face Spaces in seconds.\n\n## Productivity Apps\n\n**[Pomodoro Timer](https://huggingface.co/spaces/ysharma/pomodoro-timer)**: A focus timer where a pixel-art tree grows as you work. Starts as a seed, sprouts branches, grows leaves. Complete a session and the tree joins your forest. Session tracking, theme switching, break modes — all interactive, all in one file.\n\nThe tree animation alone would normally require a separate React component. Here it's just CSS keyframes in `css_template` and state updates in `js_on_load`.\n\n## Business Apps\n\n**[GitHub Contribution Heatmap](https://huggingface.co/spaces/ysharma/github-contribution-heatmap)**: Click any cell to toggle contributions. Multiple color themes. Pattern generators (streaks, seasonal, random). Live stats that update as you edit.\n\n**[Kanban Board](https://huggingface.co/spaces/ysharma/drag-and-drop-kanban-board)**: Full drag-and-drop between columns. Inline editing (double-click any card). Search feature that can filter in real-time. Collapsible columns.\n\nDrag-and-drop usually means pulling in a library. Here it's native HTML5 drag events wired up in `js_on_load`, with state synced back to Python via `trigger('change')`.\n\n## Creative Apps\n\n**[Spin-to-Win Wheel](https://huggingface.co/spaces/ysharma/spin-wheel)**: Smooth CSS animation, rotation state that persists across re-renders. Preset configurations for yes/no decisions, restaurant picking, team selection. You can also add custom spinning segments on the fly.\n\n## ML Apps\n\nThis is where `gr.HTML` gets really interesting for ML work: you can build specialized components that can handle your exact output format, then use them like any built-in Gradio component.\n\n**[Detection Viewer](https://huggingface.co/spaces/hysts-gradio-custom-html/detection-viewer-demo)**: A custom viewer for object detection, instance segmentation, and pose estimation results. Renders bounding boxes, segmentation masks, keypoints, and skeleton connections — all in a reusable `gr.HTML` subclass that plugs directly into your model pipeline.\n\nThe community's built some creative components with gr.HTML too:\n\n**[3D Camera Control for Image Editing](https://huggingface.co/spaces/multimodalart/qwen-image-multiple-angles-3d-camera)**: A full Three.js viewport inside a Gradio app! Drag handles to control azimuth, elevation, and distance. Your uploaded image appears in the 3D scene, and the camera parameters feed directly into Qwen's latest image editing model. These kinds of interactive 3D controls would normally require a separate frontend — with Gradio it's just one `gr.HTML` subclass🔥\n\n**[Real-time Speech Transcription](https://huggingface.co/spaces/mistralai/Voxtral-Mini-Realtime)**: Live transcription with Mistral's Voxtral model. The transcript display is a custom `gr.HTML` component with animated status badges, a live WPM counter, and styled output that updates as you speak. Real-time UI feedback without using React!\n\n* * *\n\n## How It Works\n\nEvery `gr.HTML` component takes three templates:\n\n```python\ngr.HTML(\n    value={\"count\": 0},\n    html_template=\"<button>Clicked ${value.count} times</button>\",\n    css_template=\"button { background: #667eea; color: white; }\",\n    js_on_load=\"\"\"\n        element.querySelector('button').onclick = () => {\n            props.value = { count: props.value.count + 1 };\n            trigger('change');\n        };\n    \"\"\"\n)\n```\n\n`${value}` injects Python state. `props.value` updates it from JavaScript. `trigger('change')` syncs back to Python. That's the whole API.\n\nFor reusable components, subclass `gr.HTML`:\n\n```python\nclass Heatmap(gr.HTML):\n    def __init__(self, value=None, theme=\"green\", **kwargs):\n        super().__init__(\n            value=value,\n            theme=theme,\n            html_template=TEMPLATE,\n            css_template=STYLES,\n            js_on_load=SCRIPT,\n            **kwargs\n        )\n```\n\nNow `Heatmap()` works like `gr.Image()` or `gr.Slider()` — use it in Blocks, wire up event handlers, whatever you need.\n\n## Why This Matters for Vibe Coding\n\nWhen you ask your LLM to build a custom component, single-file output is everything. No \"now create the styles file\" or \"wire this into your build config.\" Just one Python file that runs immediately.\n\nThe feedback loop becomes: describe what you want → get code → `gradio app.py` → see it working → describe what to fix → repeat. Each cycle takes seconds with [Gradio's reload mode](https://www.gradio.app/guides/developing-faster-with-reload-mode).\n\n[Deploy to Spaces](https://www.gradio.app/guides/sharing-your-app#hosting-on-hf-spaces) with `gradio deploy` or [share a temporary link](https://www.gradio.app/guides/sharing-your-app#sharing-demos) with `demo.launch(share=True)`. Within a few seconds from an idea to a live app.\n\n* * *\n\nGradio ships with 32 interactive components, but sometimes your perfect AI web app needs something special. That's where `gr.HTML` comes in.\n\nIf you’ve got an idea, try building it with `gr.HTML`: describe what you want to your LLM, generate the code, run it. You might be surprised what you can ship in 5 minutes.\n\nSuggested reading:\n\n- Gradio guide: [Custom Components with gr.HTML](https://www.gradio.app/guides/custom-HTML-components)\n- API docs: [gr.HTML](https://www.gradio.app/docs/gradio/html)\n",
  "authors": [],
  "captured_at": "2026-10-04T12:50:30.804479567+00:00",
  "category": "ai",
  "content_html": "<p><strong>Gradio 6</strong> quietly shipped a very powerful feature: <strong><code>gr.HTML</code></strong> now supports custom templates, scoped CSS, and JavaScript interactivity. Which means you can build pretty much any web component — and Claude (or any other frontier LLM) can generate the whole thing in one shot: frontend, backend, and state management, all in a single Python file.</p>\n<p>We tested this by building different types of apps. Each one is a single Python file, no build step, deployable to Hugging Face Spaces in seconds.</p>\n<h2>Productivity Apps</h2>\n<p><strong><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/spaces/ysharma/pomodoro-timer\">Pomodoro Timer</a></strong>: A focus timer where a pixel-art tree grows as you work. Starts as a seed, sprouts branches, grows leaves. Complete a session and the tree joins your forest. Session tracking, theme switching, break modes — all interactive, all in one file.</p>\n<p>The tree animation alone would normally require a separate React component. Here it's just CSS keyframes in <code>css_template</code> and state updates in <code>js_on_load</code>.</p>\n<h2>Business Apps</h2>\n<p><strong><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/spaces/ysharma/github-contribution-heatmap\">GitHub Contribution Heatmap</a></strong>: Click any cell to toggle contributions. Multiple color themes. Pattern generators (streaks, seasonal, random). Live stats that update as you edit.</p>\n<p><strong><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/spaces/ysharma/drag-and-drop-kanban-board\">Kanban Board</a></strong>: Full drag-and-drop between columns. Inline editing (double-click any card). Search feature that can filter in real-time. Collapsible columns.</p>\n<p>Drag-and-drop usually means pulling in a library. Here it's native HTML5 drag events wired up in <code>js_on_load</code>, with state synced back to Python via <code>trigger('change')</code>.</p>\n<h2>Creative Apps</h2>\n<p><strong><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/spaces/ysharma/spin-wheel\">Spin-to-Win Wheel</a></strong>: Smooth CSS animation, rotation state that persists across re-renders. Preset configurations for yes/no decisions, restaurant picking, team selection. You can also add custom spinning segments on the fly.</p>\n<h2>ML Apps</h2>\n<p>This is where <code>gr.HTML</code> gets really interesting for ML work: you can build specialized components that can handle your exact output format, then use them like any built-in Gradio component.</p>\n<p><strong><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/spaces/hysts-gradio-custom-html/detection-viewer-demo\">Detection Viewer</a></strong>: A custom viewer for object detection, instance segmentation, and pose estimation results. Renders bounding boxes, segmentation masks, keypoints, and skeleton connections — all in a reusable <code>gr.HTML</code> subclass that plugs directly into your model pipeline.</p>\n<p>The community's built some creative components with gr.HTML too:</p>\n<p><strong><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/spaces/multimodalart/qwen-image-multiple-angles-3d-camera\">3D Camera Control for Image Editing</a></strong>: A full Three.js viewport inside a Gradio app! Drag handles to control azimuth, elevation, and distance. Your uploaded image appears in the 3D scene, and the camera parameters feed directly into Qwen's latest image editing model. These kinds of interactive 3D controls would normally require a separate frontend — with Gradio it's just one <code>gr.HTML</code> subclass🔥</p>\n<p><strong><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://huggingface.co/spaces/mistralai/Voxtral-Mini-Realtime\">Real-time Speech Transcription</a></strong>: Live transcription with Mistral's Voxtral model. The transcript display is a custom <code>gr.HTML</code> component with animated status badges, a live WPM counter, and styled output that updates as you speak. Real-time UI feedback without using React!</p>\n<hr />\n<h2>How It Works</h2>\n<p>Every <code>gr.HTML</code> component takes three templates:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">gr</span><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-function syntax-python\">HTML</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n    <span class=\"syntax-variable syntax-parameter syntax-python\">value</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-structure syntax-dictionary-or-set syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-begin syntax-python\">{</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">count<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-key-value syntax-python\">:</span> <span class=\"syntax-constant syntax-numeric syntax-integer syntax-decimal syntax-python\">0</span><span class=\"syntax-punctuation syntax-section syntax-dictionary-or-set syntax-end syntax-python\">}</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">html_template</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">&lt;button&gt;Clicked $<span class=\"syntax-constant syntax-other syntax-placeholder syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-placeholder syntax-begin syntax-python\">{</span>value.count<span class=\"syntax-punctuation syntax-definition syntax-placeholder syntax-end syntax-python\">}</span></span> times&lt;/button&gt;<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">css_template</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">button { background: #667eea; color: white; }<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n    <span class=\"syntax-variable syntax-parameter syntax-python\">js_on_load</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-block syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;&quot;&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-block syntax-python\">\n        <span class=\"syntax-meta syntax-string syntax-python\"></span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-block syntax-python\">element.querySelector(&#39;button&#39;).onclick = () =&gt; {\n            props.value = { count: props.value.count + 1 };\n            trigger(&#39;change&#39;);\n        };\n    <span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;&quot;&quot;</span></span></span>\n</span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n</span></span></code></pre>\n<p><code>${value}</code> injects Python state. <code>props.value</code> updates it from JavaScript. <code>trigger('change')</code> syncs back to Python. That's the whole API.</p>\n<p>For reusable components, subclass <code>gr.HTML</code>:</p>\n<pre><code class=\"language-python\"><span class=\"code-snippet\" data-language=\"Python\"><span class=\"syntax-source syntax-python\"><span class=\"syntax-meta syntax-class syntax-python\"><span class=\"syntax-storage syntax-type syntax-class syntax-python\">class</span> <span class=\"syntax-entity syntax-name syntax-class syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">Heatmap</span></span><span class=\"syntax-meta syntax-class syntax-inheritance syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-inheritance syntax-begin syntax-python\">(</span></span></span><span class=\"syntax-meta syntax-class syntax-inheritance syntax-python\"><span class=\"syntax-entity syntax-other syntax-inherited-class syntax-python\">gr<span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span>HTML</span><span class=\"syntax-punctuation syntax-section syntax-inheritance syntax-end syntax-python\">)</span></span><span class=\"syntax-meta syntax-class syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-class syntax-begin syntax-python\">:</span></span>\n<span class=\"syntax-meta syntax-function syntax-python\">    <span class=\"syntax-storage syntax-type syntax-function syntax-python\">def</span> <span class=\"syntax-entity syntax-name syntax-function syntax-python\"><span class=\"syntax-support syntax-function syntax-magic syntax-python\">__init__</span></span></span><span class=\"syntax-meta syntax-function syntax-parameters syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-begin syntax-python\">(</span></span><span class=\"syntax-meta syntax-function syntax-parameters syntax-python\"><span class=\"syntax-variable syntax-parameter syntax-python\">self</span><span class=\"syntax-punctuation syntax-separator syntax-parameters syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">value</span></span><span class=\"syntax-meta syntax-function syntax-parameters syntax-default-value syntax-python\"><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-constant syntax-language syntax-python\">None</span></span><span class=\"syntax-meta syntax-function syntax-parameters syntax-python\"><span class=\"syntax-punctuation syntax-separator syntax-parameters syntax-python\">,</span> <span class=\"syntax-variable syntax-parameter syntax-python\">theme</span></span><span class=\"syntax-meta syntax-function syntax-parameters syntax-default-value syntax-python\"><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\"><span class=\"syntax-punctuation syntax-definition syntax-string syntax-begin syntax-python\">&quot;</span></span></span><span class=\"syntax-meta syntax-string syntax-python\"><span class=\"syntax-string syntax-quoted syntax-double syntax-python\">green<span class=\"syntax-punctuation syntax-definition syntax-string syntax-end syntax-python\">&quot;</span></span></span></span><span class=\"syntax-meta syntax-function syntax-parameters syntax-python\"><span class=\"syntax-punctuation syntax-separator syntax-parameters syntax-python\">,</span> <span class=\"syntax-keyword syntax-operator syntax-unpacking syntax-mapping syntax-python\">**</span><span class=\"syntax-variable syntax-parameter syntax-python\">kwargs</span><span class=\"syntax-punctuation syntax-section syntax-parameters syntax-end syntax-python\">)</span></span><span class=\"syntax-meta syntax-function syntax-python\"><span class=\"syntax-punctuation syntax-section syntax-function syntax-begin syntax-python\">:</span></span>\n        <span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-support syntax-function syntax-builtin syntax-python\">super</span></span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span><span class=\"syntax-meta syntax-function-call syntax-python\"><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-punctuation syntax-accessor syntax-dot syntax-python\">.</span></span><span class=\"syntax-support syntax-function syntax-magic syntax-python\">__init__</span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-begin syntax-python\">(</span><span class=\"syntax-meta syntax-function-call syntax-arguments syntax-python\">\n            <span class=\"syntax-variable syntax-parameter syntax-python\">value</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">value</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n            <span class=\"syntax-variable syntax-parameter syntax-python\">theme</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">theme</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n            <span class=\"syntax-variable syntax-parameter syntax-python\">html_template</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-other syntax-constant syntax-python\">TEMPLATE</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n            <span class=\"syntax-variable syntax-parameter syntax-python\">css_template</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-other syntax-constant syntax-python\">STYLES</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n            <span class=\"syntax-variable syntax-parameter syntax-python\">js_on_load</span><span class=\"syntax-keyword syntax-operator syntax-assignment syntax-python\">=</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-variable syntax-other syntax-constant syntax-python\">SCRIPT</span></span><span class=\"syntax-punctuation syntax-separator syntax-arguments syntax-python\">,</span>\n            <span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-python\">*</span><span class=\"syntax-keyword syntax-operator syntax-arithmetic syntax-python\">*</span><span class=\"syntax-meta syntax-qualified-name syntax-python\"><span class=\"syntax-meta syntax-generic-name syntax-python\">kwargs</span></span>\n        </span><span class=\"syntax-punctuation syntax-section syntax-arguments syntax-end syntax-python\">)</span></span>\n</span></span></code></pre>\n<p>Now <code>Heatmap()</code> works like <code>gr.Image()</code> or <code>gr.Slider()</code> — use it in Blocks, wire up event handlers, whatever you need.</p>\n<h2>Why This Matters for Vibe Coding</h2>\n<p>When you ask your LLM to build a custom component, single-file output is everything. No &quot;now create the styles file&quot; or &quot;wire this into your build config.&quot; Just one Python file that runs immediately.</p>\n<p>The feedback loop becomes: describe what you want → get code → <code>gradio app.py</code> → see it working → describe what to fix → repeat. Each cycle takes seconds with <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.gradio.app/guides/developing-faster-with-reload-mode\">Gradio's reload mode</a>.</p>\n<p><a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.gradio.app/guides/sharing-your-app#hosting-on-hf-spaces\">Deploy to Spaces</a> with <code>gradio deploy</code> or <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.gradio.app/guides/sharing-your-app#sharing-demos\">share a temporary link</a> with <code>demo.launch(share=True)</code>. Within a few seconds from an idea to a live app.</p>\n<hr />\n<p>Gradio ships with 32 interactive components, but sometimes your perfect AI web app needs something special. That's where <code>gr.HTML</code> comes in.</p>\n<p>If you’ve got an idea, try building it with <code>gr.HTML</code>: describe what you want to your LLM, generate the code, run it. You might be surprised what you can ship in 5 minutes.</p>\n<p>Suggested reading:</p>\n<ul>\n<li>Gradio guide: <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.gradio.app/guides/custom-HTML-components\">Custom Components with gr.HTML</a></li>\n<li>API docs: <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"https://www.gradio.app/docs/gradio/html\">gr.HTML</a></li>\n</ul>\n",
  "content_markdown": "**Gradio 6** quietly shipped a very powerful feature: **`gr.HTML`** now supports custom templates, scoped CSS, and JavaScript interactivity. Which means you can build pretty much any web component — and Claude (or any other frontier LLM) can generate the whole thing in one shot: frontend, backend, and state management, all in a single Python file.\n\nWe tested this by building different types of apps. Each one is a single Python file, no build step, deployable to Hugging Face Spaces in seconds.\n\n## Productivity Apps\n\n**[Pomodoro Timer](https://huggingface.co/spaces/ysharma/pomodoro-timer)**: A focus timer where a pixel-art tree grows as you work. Starts as a seed, sprouts branches, grows leaves. Complete a session and the tree joins your forest. Session tracking, theme switching, break modes — all interactive, all in one file.\n\nThe tree animation alone would normally require a separate React component. Here it's just CSS keyframes in `css_template` and state updates in `js_on_load`.\n\n## Business Apps\n\n**[GitHub Contribution Heatmap](https://huggingface.co/spaces/ysharma/github-contribution-heatmap)**: Click any cell to toggle contributions. Multiple color themes. Pattern generators (streaks, seasonal, random). Live stats that update as you edit.\n\n**[Kanban Board](https://huggingface.co/spaces/ysharma/drag-and-drop-kanban-board)**: Full drag-and-drop between columns. Inline editing (double-click any card). Search feature that can filter in real-time. Collapsible columns.\n\nDrag-and-drop usually means pulling in a library. Here it's native HTML5 drag events wired up in `js_on_load`, with state synced back to Python via `trigger('change')`.\n\n## Creative Apps\n\n**[Spin-to-Win Wheel](https://huggingface.co/spaces/ysharma/spin-wheel)**: Smooth CSS animation, rotation state that persists across re-renders. Preset configurations for yes/no decisions, restaurant picking, team selection. You can also add custom spinning segments on the fly.\n\n## ML Apps\n\nThis is where `gr.HTML` gets really interesting for ML work: you can build specialized components that can handle your exact output format, then use them like any built-in Gradio component.\n\n**[Detection Viewer](https://huggingface.co/spaces/hysts-gradio-custom-html/detection-viewer-demo)**: A custom viewer for object detection, instance segmentation, and pose estimation results. Renders bounding boxes, segmentation masks, keypoints, and skeleton connections — all in a reusable `gr.HTML` subclass that plugs directly into your model pipeline.\n\nThe community's built some creative components with gr.HTML too:\n\n**[3D Camera Control for Image Editing](https://huggingface.co/spaces/multimodalart/qwen-image-multiple-angles-3d-camera)**: A full Three.js viewport inside a Gradio app! Drag handles to control azimuth, elevation, and distance. Your uploaded image appears in the 3D scene, and the camera parameters feed directly into Qwen's latest image editing model. These kinds of interactive 3D controls would normally require a separate frontend — with Gradio it's just one `gr.HTML` subclass🔥\n\n**[Real-time Speech Transcription](https://huggingface.co/spaces/mistralai/Voxtral-Mini-Realtime)**: Live transcription with Mistral's Voxtral model. The transcript display is a custom `gr.HTML` component with animated status badges, a live WPM counter, and styled output that updates as you speak. Real-time UI feedback without using React!\n\n* * *\n\n## How It Works\n\nEvery `gr.HTML` component takes three templates:\n\n```python\ngr.HTML(\n    value={\"count\": 0},\n    html_template=\"<button>Clicked ${value.count} times</button>\",\n    css_template=\"button { background: #667eea; color: white; }\",\n    js_on_load=\"\"\"\n        element.querySelector('button').onclick = () => {\n            props.value = { count: props.value.count + 1 };\n            trigger('change');\n        };\n    \"\"\"\n)\n```\n\n`${value}` injects Python state. `props.value` updates it from JavaScript. `trigger('change')` syncs back to Python. That's the whole API.\n\nFor reusable components, subclass `gr.HTML`:\n\n```python\nclass Heatmap(gr.HTML):\n    def __init__(self, value=None, theme=\"green\", **kwargs):\n        super().__init__(\n            value=value,\n            theme=theme,\n            html_template=TEMPLATE,\n            css_template=STYLES,\n            js_on_load=SCRIPT,\n            **kwargs\n        )\n```\n\nNow `Heatmap()` works like `gr.Image()` or `gr.Slider()` — use it in Blocks, wire up event handlers, whatever you need.\n\n## Why This Matters for Vibe Coding\n\nWhen you ask your LLM to build a custom component, single-file output is everything. No \"now create the styles file\" or \"wire this into your build config.\" Just one Python file that runs immediately.\n\nThe feedback loop becomes: describe what you want → get code → `gradio app.py` → see it working → describe what to fix → repeat. Each cycle takes seconds with [Gradio's reload mode](https://www.gradio.app/guides/developing-faster-with-reload-mode).\n\n[Deploy to Spaces](https://www.gradio.app/guides/sharing-your-app#hosting-on-hf-spaces) with `gradio deploy` or [share a temporary link](https://www.gradio.app/guides/sharing-your-app#sharing-demos) with `demo.launch(share=True)`. Within a few seconds from an idea to a live app.\n\n* * *\n\nGradio ships with 32 interactive components, but sometimes your perfect AI web app needs something special. That's where `gr.HTML` comes in.\n\nIf you’ve got an idea, try building it with `gr.HTML`: describe what you want to your LLM, generate the code, run it. You might be surprised what you can ship in 5 minutes.\n\nSuggested reading:\n\n- Gradio guide: [Custom Components with gr.HTML](https://www.gradio.app/guides/custom-HTML-components)\n- API docs: [gr.HTML](https://www.gradio.app/docs/gradio/html)\n",
  "dateCreated": "2026-10-04T12:50:30.804479567+00:00",
  "date_created": "2026-02-18T00:00:00+00:00",
  "date_modified": "2026-02-18T00:00:00+00:00",
  "date_published": "2026-02-18T00:00:00+00:00",
  "description": "Gradio 6 quietly shipped a very powerful feature: gr.HTML now supports custom templates, scoped CSS, and JavaScript interactivity. Which means you can build pretty much any web component — and Claude (or any other frontier LLM) can…",
  "external_url": "https://huggingface.co/blog/gradio-html-one-shot-apps",
  "git": {
    "commit": "ebb0941c5c06a2ab2377342d607535262a3e969e",
    "path": "items/huggingface-co/2026/02/2026-02-18-one-shot-any-web-app-with-gradio-s-gr-html.md",
    "permalink": "https://github.com/aymericbeaumet/aggr-instance/blob/ebb0941c5c06a2ab2377342d607535262a3e969e/items/huggingface-co/2026/02/2026-02-18-one-shot-any-web-app-with-gradio-s-gr-html.md"
  },
  "id": "urn:aggr:item:ba8f03dbfc87bab9f9679a6c74678aedf44d0581",
  "isBasedOn": {
    "@id": "https://huggingface.co/blog/gradio-html-one-shot-apps"
  },
  "mainEntity": {
    "@id": "https://huggingface.co/blog/gradio-html-one-shot-apps",
    "@type": "CreativeWork",
    "archivedAt": {
      "@id": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-02-18-one-shot-any-web-app-with-gradio-s-gr-html/#webpage"
    },
    "dateModified": "2026-02-18T00:00:00+00:00",
    "datePublished": "2026-02-18T00:00:00+00:00",
    "headline": "One-Shot Any Web App with Gradio's gr.HTML",
    "timeRequired": "PT4M",
    "url": "https://huggingface.co/blog/gradio-html-one-shot-apps",
    "wordCount": 703
  },
  "name": "Archived snapshot: One-Shot Any Web App with Gradio's gr.HTML",
  "preview": null,
  "reading_minutes": 4,
  "replicated_at": null,
  "source": "huggingface-co",
  "source_name": "Hugging Face - Blog",
  "summary": null,
  "tags": [],
  "temporalCoverage": "2026-10-04T12:50:30.804479567+00:00",
  "title": "One-Shot Any Web App with Gradio's gr.HTML",
  "url": "https://aggr.aymericbeaumet.com/items/huggingface-co/2026-02-18-one-shot-any-web-app-with-gradio-s-gr-html/",
  "word_count": 703
}