Если вы когда-нибудь пытались сделать скролл в 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 */
}

Разбор правил

  1. touch-action: none на swipe-y-off *
    • Полностью блокирует все вертикальные свайпы в дочерних элементах.
    • Это важно для контроля scroll в Telegram MiniApp, чтобы системные свайпы не «ломали» прокрутку.
  2. overscroll-behavior: contain на swipe-y-on
    • Запрещает «прокидку» scroll наружу.
    • Например, если пользователь доскроллил до конца страницы, WebView не начинает дергать родительский контейнер.
  3. -webkit-overflow-scrolling: touch
    • Добавляет плавность scroll на iOS.
    • Без этого прокрутка может прыгать, особенно при быстром свайпе.
  4. touch-action: pan-y !important на swipe-y-on
    • Разрешает вертикальный свайп только там, где мы его включили.
    • Остальные области остаются «не свайпабельными».

Базовый провайдер приложения и инициализация Telegram SDK

Первое, с чего начинается любой нормальный MiniApp — это корневая обёртка приложения и настройка Telegram SDK. И, честно говоря, это тот момент, где у меня лично пропадала уверенность, что scroll будет работать стабильно. Но давай разберём всё пошагово.

Что делает базовый провайдер

На практике корневой компонент решает сразу несколько задач:

  1. Говорим Telegram, что приложение готово. Это делается через метод WebApp.ready(). Без этого SDK может работать некорректно, а наш скролл в Telegram MiniApp будет хаотичным.
  2. Разворачиваем приложение на весь экран. Метод WebApp.expand() гарантирует, что весь контент помещается, а scroll ведёт себя предсказуемо.
  3. Блокируем системные вертикальные свайпы 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 радует пользователей!