Если вы когда-нибудь пытались сделать скролл в Telegram MiniApp с управлением через SDK, то наверняка знаете, что WebView внутри Telegram ведёт себя совсем не так, как обычный браузер. Я сам сначала подумал: «Ну, overflow-y: auto и всё, готово!» — и был уверен, что scroll будет работать как в привычном React-приложении.
Но как только приложение открывалось внутри Telegram, начинались странные дёргания, bounce на iOS, рывки на Android, а иногда MiniApp закрывался свайпом вниз.
И тут я понял ключевую вещь: Telegram MiniApp работает через WebView, и если не забрать контроль над свайпами и скроллом через SDK, интерфейс будет хаотичным и непредсказуемым.
Поэтому перед нами стоит задача: сделать scroll стабильным, плавным и управляемым, при этом не теряя контроль над всей навигацией в приложении.
В данной статье, в примерах, я использую CSS фреймворк Tailwind 4. Если вы не знакомы с его классами, советую пройти краткий курс по его основам.
В следующих шагах я подробно покажу, как я организовал корневую обёртку, правильно инициализировал SDK Telegram и создал надёжную базу для предсказуемого скролла внутри React.
Базовая схема организации в корневой обвертке
BrowserRouter
└── Корневой div (swipe-y-off, overflow-hidden, max-w-[440px], min-h-screen, mx-auto)
├── Инициализация SDK (WebApp.ready(), WebApp.expand(), WebApp.disableVerticalSwipes())
└── AppRouter
├── Страница 1
│ └── Scroll-контейнер (swipe-y-on, overflow-y-auto, height: 100vh)
│ ├── Основной контент (h-full)
│ └── Буфер 1px
├── Страница 2
│ └── Scroll-контейнер (swipe-y-on, overflow-y-auto, height: 100vh)
│ ├── Основной контент (h-full)
│ └── Буфер 1px
└── Страница N
└── Scroll-контейнер (swipe-y-on, overflow-y-auto, height: 100vh)
├── Основной контент (h-full)
└── Буфер 1px
Обязательные CSS правила, чтобы вертикальные свайпы и скролл в Telegram MiniApp корректно работал.
Вставьте этот CSS в корневой файл проекта со стилями чтобы эти классы были доступны глобально в приложении.
.swipe-y-off * {
touch-action: none; /* блокируем все вертикальные свайпы */
}
.swipe-y-off .swipe-y-on,
.swipe-y-off .swipe-y-on * {
overscroll-behavior: contain; /* scroll не прокидывается наружу */
-webkit-overflow-scrolling: touch; /* плавная прокрутка на iOS */
touch-action: pan-y !important; /* разрешаем вертикальный swipe */
}
Разбор правил
touch-action: noneнаswipe-y-off *- Полностью блокирует все вертикальные свайпы в дочерних элементах.
- Это важно для контроля scroll в Telegram MiniApp, чтобы системные свайпы не «ломали» прокрутку.
overscroll-behavior: containнаswipe-y-on- Запрещает «прокидку» scroll наружу.
- Например, если пользователь доскроллил до конца страницы, WebView не начинает дергать родительский контейнер.
-webkit-overflow-scrolling: touch- Добавляет плавность scroll на iOS.
- Без этого прокрутка может прыгать, особенно при быстром свайпе.
touch-action: pan-y !importantнаswipe-y-on- Разрешает вертикальный свайп только там, где мы его включили.
- Остальные области остаются «не свайпабельными».
Базовый провайдер приложения и инициализация Telegram SDK
Первое, с чего начинается любой нормальный MiniApp — это корневая обёртка приложения и настройка Telegram SDK. И, честно говоря, это тот момент, где у меня лично пропадала уверенность, что scroll будет работать стабильно. Но давай разберём всё пошагово.
Что делает базовый провайдер
На практике корневой компонент решает сразу несколько задач:
- Говорим Telegram, что приложение готово. Это делается через метод
WebApp.ready(). Без этого SDK может работать некорректно, а наш скролл в Telegram MiniApp будет хаотичным. - Разворачиваем приложение на весь экран. Метод
WebApp.expand()гарантирует, что весь контент помещается, а scroll ведёт себя предсказуемо. - Блокируем системные вертикальные свайпы Telegram. Без
WebApp.disableVerticalSwipes()любое вертикальное движение могло бы закрыть MiniApp, а вместе с ним — и весь наш аккуратный скролл.
Но есть один нюанс, о котором важно помнить: React не любит прямое обновление состояния внутри useEffect. Поэтому я оборачиваю setReady(true) в микротаск через queueMicrotask. Это маленькая деталь, но без неё появляются баги или странные предупреждения.
Пример кода базового провайдера
Вот как выглядит полный компонент AppProvider:
import { useState, useEffect } from "react";
import WebApp from "@twa-dev/sdk";
import { BrowserRouter } from "react-router-dom";
import { AppRouter } from "./Routes";
export function AppProvider() {
const [ready, setReady] = useState(false);
useEffect(() => {
WebApp.ready(); // Telegram: "готово"
WebApp.expand(); // разворачиваем на весь экран
WebApp.disableVerticalSwipes(); // блокируем системные свайпы
// Микротаск нужен, чтобы React корректно обновил состояние
queueMicrotask(() => setReady(true));
}, []);
// Пока SDK не готов — ничего не рендерим
if (!ready) return null;
return (
<BrowserRouter>
<div className="swipe-y-off overflow-hidden max-w-[440px] min-h-screen mx-auto">
<AppRouter /> {/* Здесь подключаем маршруты вашего MiniApp */}
</div>
</BrowserRouter>
);
}
Объяснение деталей
if (!ready) return null— это наш маленький «страж». Пока SDK не готов, пользователь не видит контент. На практике сюда можно вставить спиннер или заставку.- Корневой div с классом
swipe-y-off— это фундамент для управления scroll в Telegram MiniApp. Он блокирует системные свайпы, чтобы мы могли полностью контролировать прокрутку внутри своих контейнеров. - CSS-классы
overflow-hidden,max-w-[440px],min-h-screen,mx-auto— гарантируют, что контейнер занимает весь экран, ширина ограничена для мобильных устройств, а интерфейс выглядит аккуратно и предсказуемо.
Разбор корневых CSS-классов обвертки: фундамент для того что скролл в Telegram MiniApp был стабильным
После того как мы настроили базовый провайдер и инициализировали SDK, приходит время разобраться с тем, как именно CSS помогает управлять scroll в Telegram MiniApp.
И да, именно на этом этапе я понял, что без правильной обёртки любой scroll ведёт себя хаотично: WebView Telegram пытается вмешиваться в прокрутку, дёргает контент, а интерфейс начинает «прыгать». Поэтому ключевые классы — это наш щит и фундамент одновременно.
swipe-y-off — блокируем все вертикальные свайпы
Этот класс — главный герой. Почему?
- Он полностью отключает системные вертикальные свайпы Telegram для всех дочерних элементов.
- Любой контейнер внутри автоматически становится «не свайпабельным».
- Благодаря этому мы получаем полный контроль над scroll внутри наших элементов, что крайне важно для стабильного скролла в Telegram MiniApp.
И вот небольшой лайфхак: если внутри контейнера нужен scroll, мы потом ставим класс swipe-y-on — об этом подробнее на следующем шаге.
overflow-hidden — изоляция scroll от Telegram
Без этого свойства Telegram WebView пытается применить свою физику прокрутки, и тогда наш scroll в Telegram MiniApp начинает дергаться.
overflow-hiddenзапрещает системный scroll.- Все внутренние scroll-контейнеры становятся независимыми.
- В сочетании с
swipe-y-onвнутри страниц мы полностью контролируем, где и как работает прокрутка.
Ограничение размеров: max-w-[440px] и min-h-screen
Здесь всё просто, но важно:
max-w-[440px]— ограничивает максимальную ширину приложения. Особенно актуально для Telegram, где MiniApp чаще всего показывается на мобильных устройствах.min-h-screen— минимальная высота контейнера равна экрану. Это гарантирует, что scroll-контейнер всегда занимает весь экран, даже если контента мало.
Эта комбинация обеспечивает стабильность scroll и аккуратный визуальный вид приложения.
Центрирование: mx-auto
- Довольно простая вещь, но важная визуально: центрирует контейнер горизонтально на широких экранах.
- В сочетании с другими классами создаёт аккуратный и предсказуемый интерфейс.
Именно эта комбинация создаёт фундамент для стабильного scroll в Telegram MiniApp, на который мы потом навешиваем страницы с swipe-y-on и микробуфером в 1px.
Страницы и scroll-контейнеры: открываем прокрутку там, где нужно
Итак, у нас есть базовая обёртка с блокировкой системных свайпов и фундамент для предсказуемого scroll. Но если мы оставим всё как есть, scroll в Telegram MiniApp просто не будет работать на конкретных страницах.
Поэтому следующий шаг — создавать отдельные scroll-контейнеры для каждой страницы, где прокрутка действительно нужна. Это позволяет Telegram больше не вмешиваться, а пользователю — плавно листать контент.
Пример страницы с scroll-контейнером
export default function Page() {
return (
<div
className="swipe-y-on overflow-y-auto"
style={{ height: "100vh" }}
>
<div className="h-full flex flex-col">
<h1>Заголовок страницы</h1>
<p>Вот тут основной контент, который можно прокручивать...</p>
<p>Добавим ещё немного контента для демонстрации scroll.</p>
</div>
{/* Буфер в 1px — делает scroll стабильным */}
<div className="h-[1px]" />
</div>
);
}
Почему это важно
swipe-y-onоткрывает прокрутку только для нужного контейнера.- Высота 100vh занимает весь экран, чтобы scroll работал корректно даже при маленьком количестве контента.
- Микробуфер 1px стабилизирует физику scroll и делает прокрутку в Telegram MiniApp почти нативной.
Благодаря такому подходу каждая страница становится предсказуемой: интерфейс ведёт себя плавно, Telegram больше не вмешивается, а пользователь получает комфортный scroll.
Вы конечно спросите, зачем нужна вся эта игра и классами swipe-y-off и swipe-y-on. Ответ есть и он очень прост. В реальном проекте у вас появяться блоки с абсолютным позиционированием (header, bottomNav, а также возможны пробелы между блоков в виде padding или margin) и без реализации данного похода они постоянно будут дергать WebView при свайпах.
P.S. Если вы будете аккуратно следовать всем рекомендациям, правильно расставите контейнеры и обёртки, то ваш скролл в Telegram MiniApp будет работать ровно так, как задумано. Будьте внимательны, проверяйте вложенность, и тогда интерфейс станет плавным и предсказуемым. Желаю вам успехов и вдохновения в создании новых проектов — пусть каждый MiniApp радует пользователей!