Gradio gr.HTML: создание веб-приложений на Python без фронтенда | AiManual
AiManual Logo Ai / Manual.
18 Фев 2026 Инструмент

Gradio gr.HTML: как создать любое веб-приложение одним Python-файлом (без React и сборки)

Полное руководство по использованию gr.HTML в Gradio 6 для создания сложных веб-интерфейсов без React, сборки и фронтенд-разработки.

Серьезно? Веб-приложение в одном Python-файле?

Да. И это работает лучше, чем вы думаете. Gradio 6, вышедший в конце 2024 года, тихо добавил gr.HTML — компонент, который меняет правила игры. Больше не нужен React, Vue, Webpack, npm install или даже базовые знания JavaScript.

💡
Gradio 6 принес не только улучшения в производительности, но и полностью переработанную систему компонентов. gr.HTML теперь поддерживает scoped CSS, кастомные JavaScript события и даже асинхронные обновления DOM. Это уже не просто «вставка HTML», а полноценный фреймворк внутри фреймворка.

Что умеет gr.HTML в 2026 году

Если вы все еще думаете, что это просто <div>ваш html</div>, вы отстали на два года.

  • Scoped CSS — стили не утекают за пределы компонента. Пишите как в Vue SFC, но без сборки
  • JavaScript событияjs_on_load, js_on_change, полный доступ к DOM API
  • Двусторонняя связь — обновляйте HTML из Python, ловите события из браузера
  • Кастомные шаблоны — загружайте HTML из файлов, генерируйте через Jinja2
  • Поддержка современных CSS — Grid, Flexbox, CSS Variables, даже CSS-in-JS если захотите

Почему это убивает React для AI-прототипов

Создаете демо для новой модели? Пока фронтендер настраивает сборку, вы уже развернули приложение на Hugging Face Spaces.

React/Next.js Gradio + gr.HTML
node_modules (200MB+) requirements.txt (одна строка)
Webpack/Vite конфиг Никакого конфига
Деплой на Vercel gradio deploy или HF Spaces
Горячая перезагрузка через плагины Встроенная горячая перезагрузка

Не поймите меня неправильно — для масштабных продуктов React все еще король. Но для прототипов, демо, внутренних инструментов и AI-интерфейсов? Это overkill уровня «убить комара кувалдой».

Сравнение не совсем честное — React делает больше. Но в 95% случаев AI-разработчикам не нужны эти «больше». Им нужен работающий интерфейс сегодня, а не через неделю.

Реальные примеры, а не «hello world»

1 Pomodoro Timer с CSS анимациями

Таймер помодоро — идеальный тест. Нужны: анимации (CSS keyframes), таймер (setInterval), управление состоянием (старт/пауза/сброс). В React это компонент на 100 строк. В Gradio — 50 строк Python и HTML вместе.

Секрет в js_on_load:

import gradio as gr
from datetime import datetime

def create_timer():
    html = """
    <style>
    @keyframes pulse {
        0% { opacity: 1; }
        50% { opacity: 0.7; }
        100% { opacity: 1; }
    }
    .timer-active { animation: pulse 2s infinite; }
    </style>
    
    <div id="timer-container" class="p-6 rounded-xl bg-gradient-to-r from-blue-50 to-indigo-50">
        <div id="time-display" class="text-5xl font-mono text-center mb-4">25:00</div>
        <div class="flex gap-2 justify-center">
            <button onclick="startTimer()" class="px-4 py-2 bg-green-500 text-white rounded-lg">Старт</button>
            <button onclick="pauseTimer()" class="px-4 py-2 bg-yellow-500 text-white rounded-lg">Пауза</button>
            <button onclick="resetTimer()" class="px-4 py-2 bg-red-500 text-white rounded-lg">Сброс</button>
        </div>
    </div>
    
    <script>
    let timerInterval;
    let timeLeft = 25 * 60;
    
    function startTimer() {
        document.getElementById('timer-container').classList.add('timer-active');
        timerInterval = setInterval(updateTimer, 1000);
    }
    
    function updateTimer() {
        if (timeLeft <= 0) {
            clearInterval(timerInterval);
            alert('Время вышло!');
            return;
        }
        timeLeft--;
        const minutes = Math.floor(timeLeft / 60);
        const seconds = timeLeft % 60;
        document.getElementById('time-display').textContent = 
            `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
    }
    </script>
    """
    
    return gr.HTML(html)

with gr.Blocks() as demo:
    create_timer()

demo.launch()

Видите? Никакого состояния в Python, весь UI живет в браузере. Но при этом мы можем в любой момент отправить данные в Python — например, логировать сессии или интегрировать с AI-ассистентом.

2 Kanban Board с drag-and-drop

«Это точно потребует React», — скажете вы. А вот и нет. HTML5 Drag and Drop API работает без библиотек.

Хитрость в том, чтобы использовать gr.on для обработки событий:

import json
import gradio as gr

def kanban_app():
    html = """
    <style>
    .column { 
        min-height: 400px;
        background: #f8fafc;
        border-radius: 12px;
        padding: 16px;
    }
    .task {
        background: white;
        padding: 12px;
        margin: 8px 0;
        border-radius: 8px;
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        cursor: move;
    }
    .task:hover {
        box-shadow: 0 4px 8px rgba(0,0,0,0.15);
    }
    </style>
    
    <div class="grid grid-cols-3 gap-6 p-6">
        <div class="column" id="todo" ondrop="drop(event)" ondragover="allowDrop(event)">
            <h3 class="text-lg font-bold mb-4">To Do</h3>
            <div class="task" draggable="true" id="task1" ondragstart="drag(event)">Написать код</div>
        </div>
        
        <div class="column" id="progress" ondrop="drop(event)" ondragover="allowDrop(event)">
            <h3 class="text-lg font-bold mb-4">In Progress</h3>
        </div>
        
        <div class="column" id="done" ondrop="drop(event)" ondragover="allowDrop(event)">
            <h3 class="text-lg font-bold mb-4">Done</h3>
        </div>
    </div>
    
    <script>
    function allowDrop(ev) {
        ev.preventDefault();
    }
    
    function drag(ev) {
        ev.dataTransfer.setData("text", ev.target.id);
    }
    
    function drop(ev) {
        ev.preventDefault();
        const data = ev.dataTransfer.getData("text");
        const task = document.getElementById(data);
        ev.target.appendChild(task);
        
        // Отправляем обновление в Python
        const boardState = {
            todo: Array.from(document.getElementById('todo').children)
                .filter(el => el.classList.contains('task'))
                .map(el => el.id),
            progress: Array.from(document.getElementById('progress').children)
                .filter(el => el.classList.contains('task'))
                .map(el => el.id),
            done: Array.from(document.getElementById('done').children)
                .filter(el => el.classList.contains('task'))
                .map(el => el.id)
        };
        
        // Магия Gradio - вызываем Python функцию
        updateBoard(boardState);
    }
    </script>
    """
    
    return gr.HTML(html)

def save_board_state(state_json):
    state = json.loads(state_json)
    print(f"Board updated: {state}")
    # Здесь можно сохранить в БД или отправить куда-то
    return "Состояние сохранено"

with gr.Blocks() as demo:
    html_component = kanban_app()
    output = gr.Textbox(label="Статус")
    
    # Связываем JavaScript с Python
    html_component.js_on(
        "updateBoard",  # имя события из JS
        save_board_state,  # Python функция
        output  # куда вывести результат
    )

demo.launch()

Это тот самый момент, когда понимаешь — границы между фронтендом и бэкендом стираются. JavaScript дергает Python функции, Python может обновлять DOM. Все в одном файле.

Когда использовать (а когда нет)

gr.HTML не серебряная пуля. Вот когда он сияет:

  • AI-демо — показать работу модели с кастомной визуализацией
  • Внутренние инструменты — админки, дашборды, утилиты для команды
  • Быстрые прототипы — проверить идею до привлечения фронтендера
  • Образовательные проекты — студенты учатся логике, а не сборке
  • Микросервисы с UI — каждый сервис со своим минимальным интерфейсом

А вот когда бежать прочь:

  • Мобильное приложение (хотя PWA возможно)
  • Комплексный SPA с роутингом на 10+ страниц
  • Высоконагруженный публичный сервис
  • Когда в команде уже есть фронтендеры (не обижайте коллег)

Под капотом: как это вообще работает

Gradio 6 использует два механизма для gr.HTML:

  1. Shadow DOM для изоляции — ваши стили не конфликтуют с другими компонентами
  2. Message passing через WebSockets — JavaScript ↔ Python общение
  3. Динамическая перезагрузка — меняете код, интерфейс обновляется без перезагрузки страницы

С февраля 2025 года Gradio поддерживает gr.CustomComponent — можно создавать переиспользуемые компоненты на основе gr.HTML и публиковать их как pip-пакеты. Эко-система растет.

Интеграция с AI-ассистентами

Вот где начинается магия. Представьте:

  1. Вы описываете интерфейс текстом Claude или ChatGPT
  2. AI генерирует HTML/CSS/JS (как в статье про Syntux)
  3. Вставляете в gr.HTML
  4. Запускаете — интерфейс работает

Не нужно знать фронтенд. Не нужно дебажить сборку. Не нужно ждать CI/CD. Это Gizmo, но для серьезных приложений.

Хотите копнуть глубже? В статье «Фронтенд без фронтендера» похожий подход, но там все еще нужна сборка. Здесь — вообще ничего.

Производительность: а не тормозит ли?

Справедливый вопрос. Ответ: зависит.

Для интерфейсов с десятками компонентов — работает как родной. Gradio 6 переписали на Preact (легковесный React), и рендеринг стал быстрее на 40%.

Проблемы начинаются, если вы пытаетесь вставить 10 000 строк таблицы в один gr.HTML. Но в этом случае проблема не в Gradio, а в архитектуре.

💡
Совет: для больших таблиц используйте gr.Dataframe с виртуализацией. Для графов — gr.Plot. gr.HTML — для кастомных компонентов, которые нельзя сделать стандартными.

Деплой: от локальной машины до облака за 5 минут

Вот почему я люблю Gradio:

# Локально
gradio app.py

# На Hugging Face Spaces (бесплатно)
gradio deploy --title "Мое AI-приложение"

# Своя инфраструктура
docker build -t my-app .
docker run -p 7860:7860 my-app

Hugging Face Spaces в 2026 году — это не просто хостинг. Это discovery platform, где люди находят ваши демо. Выложили крутое приложение? Получите звезды, форки, может даже контрибьюторов.

Ограничения и подводные камни

Без розовых очков:

  • SEO — нет. Это SPA, Google не увидит контент
  • Браузерная история — роутинг нужно писать вручную
  • TypeScript — только JavaScript (хотя можно подключить через CDN)
  • Hot reload для HTML — работает, но иногда нужно обновить страницу
  • Отладка — DevTools показывают Shadow DOM, нужно привыкнуть

Но главное ограничение — ментальное. Вы привыкнете к простоте и будете раздражаться, когда придется возвращаться к «нормальной» фронтенд-разработке.

Что дальше? Будущее gr.HTML

По слухам (и по roadmap на GitHub):

  • Генерация компонентов из скриншотов — загрузите макет Figma, получите gr.HTML
  • Библиотека готовых компонентов — как MUI, но для Gradio
  • Еще глубже интеграция с AI — «создай интерфейс для классификации текста» → готовое приложение
  • Поддержка WebAssembly — выполняйте тяжелую логику в браузере

Уже сейчас можно подключить llama.cpp WebUI как компонент. Или встроить Qwen2.5 прямо в интерфейс.

Начните сегодня

Не нужно переписывать существующие проекты. Начните с малого:

  1. Возьмите простой интерфейс из вашего приложения
  2. Перепишите его на gr.HTML в отдельном файле
  3. Сравните сложность и скорость разработки
  4. Примите решение

Мой прогноз: к концу 2026 года 30% AI-демо на Hugging Face будут использовать gr.HTML для кастомных интерфейсов. Потому что когда есть молоток, который забивает все гвозди, зачем искать отвертку?

P.S. Если застряли — посмотрите исходный код популярных Spaces. В 90% случаев это один Python-файл. Без package.json. Без tsconfig. Без головной боли.