npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

nuxt-telegram-mini-app

v0.0.2

Published

Structure and configurations for building a Telegram Mini App using Nuxt and TailWind, hosted on Netlify.

Readme

🚀 Nuxt Telegram Mini App Template

A comprehensive template for building Telegram Mini Apps using Nuxt 4, Vue 3, TypeScript, and Tailwind CSS, ready for deployment on Netlify.

License Nuxt Vue TypeScript Tailwind

✨ Features

🎯 Core Features

  • 🔧 Complete Nuxt 4 Setup - Latest Nuxt with TypeScript support
  • 📱 Telegram WebApp SDK - Full integration with Telegram Mini App APIs
  • 🎨 Tailwind CSS - Utility-first CSS framework with Telegram theme integration
  • ⚡ SPA Mode - Optimized for Telegram Mini App deployment
  • 🌐 Netlify Ready - Pre-configured for seamless deployment

🧩 Components Library

  • TgButton - Telegram-styled buttons with haptic feedback
  • TgCell - List cells with navigation and interaction support
  • TgContent - Main content wrapper with proper spacing
  • TgNav - Bottom navigation bar with up to 4 menu options and icons
  • TgSection - Content sections with proper styling
  • Hero - Header component for pages

📡 Telegram SDK Integration

  • 🎮 Haptic Feedback - Impact, notification, and selection feedback
  • 🔄 Main Button - Configurable main action button
  • ⬅️ Back Button - Navigation back button control
  • 👤 User Data - Access to Telegram user information
  • 🎨 Theme Integration - Automatic Telegram theme colors
  • 📐 Viewport Control - Responsive viewport management
  • 🔗 Deep Linking - External and Telegram link handling
  • 📤 Sharing - Built-in sharing functionality

🧪 Testing & Quality

  • ⚡ Vitest - Fast unit testing framework
  • 🧪 Component Tests - Comprehensive test coverage
  • 🔍 TypeScript - Full type safety
  • 📋 Type Definitions - Complete Telegram WebApp types

🚀 Quick Start

Prerequisites

  • Node.js 18+
  • npm or yarn or pnpm

1. Use This Template

Click the "Use this template" button on GitHub or clone the repository:

git clone https://github.com/patricktobias86/nuxt-telegram-mini-app.git my-telegram-app
cd my-telegram-app

2. Install Dependencies

npm install
# or
yarn install
# or
pnpm install

3. Development

npm run dev

Open http://localhost:3000 in your browser to see the demo.

4. Build for Production

npm run generate

📁 Project Structure


├── app/                     # Nuxt app directory
│   ├── assets/             # CSS and static assets
│   │   └── css/
│   │       ├── main.css    # Global styles
│   │       └── tailwind.css # Tailwind imports
│   ├── components/         # Vue components
│   │   ├── Hero.vue        # Hero component
│   │   ├── ErrorBoundary.vue
│   │   └── tg/             # Telegram components
│   │       ├── Button.vue  # Telegram button
│   │       ├── Cell.vue    # List cell
│   │       ├── Content.vue # Content wrapper
│   │       ├── Nav.vue     # Bottom navigation
│   │       └── Section.vue # Content section
│   ├── composables/        # Vue composables
│   │   └── telegram.ts     # Telegram SDK integration
│   ├── pages/              # App pages
│   │   ├── index.vue       # Home page with SDK demo
│   │   ├── components.vue  # Components showcase
│   │   ├── utilities.vue   # Utilities demo
│   │   └── functions.vue     # Functions page
│   ├── types/              # TypeScript definitions
│   │   └── telegram-webapp.ts
│   └── utils/              # Utility functions
│       └── color.ts        # Color conversion utilities
├── server/                 # Nuxt server API
│   └── api/
│       └── verify-telegram-data.post.ts # Telegram data verification
├── public/                 # Static assets
├── tests/                  # Test files
│   └── telegram.spec.ts
├── nuxt.config.ts          # Nuxt configuration
├── tailwind.config.ts      # Tailwind configuration
└── vitest.config.ts        # Test configuration

🎯 Usage Guide

Creating Your First Page

  1. Create a new page in app/pages/:
<!-- app/pages/my-page.vue -->
<template>
  <TgContent>
    <Hero
      title="My Page"
      subtitle="Description of my page"
      image-src="/img/hero-user.svg"
    />
    
    <TgSection title="My Section" inset>
      <TgCell
        title="My Cell"
        subtitle="Cell description"
        icon="i-heroicons-star-20-solid"
      />
    </TgSection>
  </TgContent>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'

const router = useRouter()
const goHome = () => router.push('/')
</script>

Using Telegram SDK Features

<script setup lang="ts">
import { 
  useMainButton, 
  useBackButton, 
  useHapticFeedback,
  useInitData,
  useThemeParams 
} from '~/composables/telegram'

const main = useMainButton()
const back = useBackButton()
const haptic = useHapticFeedback()
const init = useInitData()
const theme = useThemeParams()

// Configure main button
onMounted(() => {
  main.mount()
  main.setParams({
    is_visible: true,
    is_active: true,
    text: 'My Action'
  })
  
  const off = main.onClick(() => {
    haptic.impactOccurred('medium')
    // Your action here
  })
  
  onBeforeUnmounted(() => off?.())
})

// Access user data
const userName = computed(() => init.user.value?.first_name || 'Guest')

// Use theme colors
const bgColor = computed(() => theme.backgroundColor.value)
</script>

Custom Components

The template includes pre-built Telegram-styled components:

<!-- Buttons -->
<TgButton title="Primary" status="primary" haptic @click="handleClick" />
<TgButton title="Secondary" status="secondary" haptic="impact-light" />
<TgButton title="Outline" status="outline" />
<TgButton title="Destructive" status="destructive" />

<!-- Cells -->
<TgCell title="Basic Cell" subtitle="With subtitle" />
<TgCell title="With Icon" icon="i-heroicons-star-20-solid" />
<TgCell title="Navigable" to="/target-page" />
<TgCell title="Interactive" @click="handleCellClick" />

<!-- Sections -->
<TgSection title="My Section" inset>
  <!-- Section content -->
</TgSection>

🧩 Using as a Nuxt Layer

This template is compatible with Nuxt Layers, allowing you to use it as a reusable layer in other Nuxt projects or extend it with additional layers.

What are Nuxt Layers?

Nuxt Layers provide a way to extend and customize Nuxt applications by sharing configurations, components, pages, composables, and more across multiple projects. Layers can be local directories or published npm packages.

Using This Template as a Layer

To use this Telegram Mini App template as a layer in another Nuxt project, install it as an NPM package:

  1. Install the NPM package:

    npm install nuxt-telegram-mini-app
    # or
    yarn add nuxt-telegram-mini-app
    # or
    pnpm add nuxt-telegram-mini-app
  2. Configure your Nuxt project to extend this layer:

    // nuxt.config.ts
    export default defineNuxtConfig({
      extends: [
        'nuxt-telegram-mini-app'
      ],
      // Your custom config here
    })

    Alternatively, if you prefer to use a local clone or git dependency:

    # Clone as a subdirectory
    git clone https://github.com/patricktobias86/nuxt-telegram-mini-app.git layers/telegram-app
    
    # Or add as git dependency in package.json:
    # "dependencies": {
    #   "nuxt-telegram-mini-app": "github:patricktobias86/nuxt-telegram-mini-app"
    # }

    Then configure:

    // nuxt.config.ts
    export default defineNuxtConfig({
      extends: [
        './layers/telegram-app'
        // or 'nuxt-telegram-mini-app' for git dependency
      ],
      // Your custom config here
    })
  3. Access layer features in your project:

    • Components: Use <TgButton>, <TgCell>, etc. in your pages
    • Composables: Import useMainButton, useHapticFeedback, etc.
    • Pages: Extend or override existing pages
    • Styles: Inherit Tailwind and Telegram theme integration

Extending This Layer

This template includes an extends: [] configuration, allowing you to further extend it with additional layers:

// nuxt.config.ts in this template
export default defineNuxtConfig({
  extends: [
    // Add your custom layers here
    // './layers/my-custom-layer',
    // 'my-published-layer'
  ],
  // ... rest of config
})

Layer Structure

The template follows Nuxt's layer conventions:

  • app/ directory contains all extendable content
  • nuxt.config.ts at root level defines layer configuration
  • Components, pages, and composables are automatically merged

Best Practices

  • Override selectively: Only override what you need to customize
  • Maintain compatibility: Keep Telegram SDK integration intact
  • Test thoroughly: Ensure Telegram features work in your extended app
  • Version control: Pin layer versions for stability

🚀 Deployment

Manual Deployment

  1. Build the project:
npm run generate
  1. Deploy the dist folder to your hosting provider

Netlify (SPA routing)

  • This project uses Vue Router history mode to preserve Telegram's #tgWebAppData hash.
  • Netlify requires a SPA redirect so deep links resolve to index.html.
  • Included file: public/_redirects with /* /index.html 200.
  • If you deploy elsewhere, add an equivalent history fallback rule.

Environment Variables

Set these in your deployment platform:

ENV=production

🧪 Testing

Run tests with:

# Run tests once
npm run test

# Run tests in watch mode
npm run test:watch

The project includes:

  • Unit tests for Telegram composables
  • Component tests for UI components
  • Integration tests for user flows

🎨 Customization

Theme Customization

The template automatically inherits Telegram's theme colors. Customize in app/assets/css/main.css:

:root {
  /* Telegram theme variables are automatically set */
  --custom-color: #your-color;
}

Tailwind Configuration

Extend Tailwind in tailwind.config.ts:

export default {
  theme: {
    extend: {
      colors: {
        'custom': '#your-color',
      }
    }
  }
}

Adding New Components

  1. Create component in app/components/
  2. Follow the existing patterns for styling and props
  3. Add TypeScript interfaces for props
  4. Include tests in tests/

📖 API Reference

Composables

useTelegramWebApp()

  • webApp - WebApp instance
  • isReady - Ready state
  • isAvailable - Availability check

useMainButton()

  • setParams(params) - Configure button
  • onClick(callback) - Handle clicks
  • visible - Visibility state

useBackButton()

  • show() / hide() - Control visibility
  • onClick(callback) - Handle clicks

useHapticFeedback()

  • impactOccurred(style) - Trigger impact
  • notificationOccurred(type) - Trigger notification
  • selectionChanged() - Trigger selection

useInitData()

  • user - User information
  • queryId - Query ID
  • startParam - Start parameter

useThemeParams()

  • backgroundColor - Theme background
  • textColor - Theme text color
  • buttonColor - Theme button color
  • And more theme colors...

Components

<TgButton>

Props-driven styling so you don’t need extra Tailwind classes.

<TgButton
  title="Label"
  status="primary|secondary|outline|danger|destructive"
  size="sm|md|lg"
  :block="true"
  :loading="false"
  :disabled="false"
  icon="i-heroicons-star-20-solid"
  icon-position="left|right"
  elevated
  uppercase
  haptic="selection|impact-light|impact-medium|impact-heavy|notification-success|notification-warning|notification-error"
  @click="handleClick"
/>

TgButton props

| Prop | Required | Default | Description | | --- | --- | --- | --- | | title | yes | — | Button label text | | status | no | primary | Visual style variant | | size | no | md | Size of the button | | block | no | true | Full width when true | | loading | no | false | Shows spinner and disables | | disabled | no | false | Disables interaction | | icon | no | — | Icon name for @nuxt/icon | | icon-position | no | left | Icon placement relative to text | | elevated | no | false | Adds a subtle shadow | | uppercase | no | false | Uppercase label | | to | no | — | Internal route, uses NuxtLink | | href | no | — | External link, uses <a> | | share-url | no | — | Triggers Telegram share on click | | haptic | no | false | Haptic feedback type or boolean |

Notes:

  • Prefer to (router) for internal navigation to avoid conflicts with bottom Nav.
  • small is still supported but size is preferred.

<TgCell>

<TgCell
  title="Cell Title"
  subtitle="Cell Subtitle"
  :description="dynamicDescription"
  icon="i-heroicons-star-20-solid"
  color="var(--tg-theme-link-color)"
  icon-color="#888"
  tone="default|secondary"
  :border="true"
  :clickable="false"    
  :chevron="undefined|true|false"  
  to="/navigation-target"         
  href="https://example.com"
  @click="handleClick"
/>

TgCell props

| Prop | Required | Default | Description | | --- | --- | --- | --- | | title | no | '' | Title text | | subtitle | no | — | Subtitle text | | description | no | — | Description text | | icon | no | — | Icon name for @nuxt/icon | | color | no | — | Title color override | | icon-color | no | — | Icon color override | | line-clamp | no | 0 | Clamp lines for text (0 = none) | | border | no | true | Bottom divider line | | tone | no | default | Background tone | | clickable | no | false | Hover style even without link | | chevron | no | auto | Force chevron visibility | | to | no | — | Internal route, uses NuxtLink | | href | no | — | External link, uses <a> |

<TgContent>

<TgContent as="main|section|div" />

Behavior

  • Automatically adds bottom safe-area padding when a <TgNav> exists on the page.
  • Default container styles: max-w-2xl, p-4, and space-y-6.

TgContent props

| Prop | Required | Default | Description | | --- | --- | --- | --- | | as | no | main | Render element | | max-width-class | no | — | Optional override for container max width | | class | no | '' | Extra classes to merge |

<TgSection>

<TgSection title="Section" inset tone="default|secondary" :append-border="true" />

Behavior

  • Rounded corners by default; larger rounding when inset.
  • No outer border around the body.

TgSection props

| Prop | Required | Default | Description | | --- | --- | --- | --- | | title | no | — | Optional section header | | inset | no | false | Indented, iOS-like style | | tone | no | default | Background tone for body | | append-border | no | true | Thin border above append slot | | class | no | '' | Extra classes on wrapper |

<TgNav>

<TgNav
  :items="navItems"
  :model-value="activeKey"
  tone="default|secondary"
  :border="true"
  height="12|14"
  :safe-area="true"
  root-class="custom-nav-class"
  @select="handleSelect"
  @update:model-value="handleActiveChange"
/>

TgNav props

| Prop | Required | Default | Description | | --- | --- | --- | --- | | items | yes | — | List of items { key, label, icon?, to? } | | model-value | no | — | Controlled active key | | tone | no | default | Background tone | | border | no | true | Top border visibility | | height | no | 14 | Item height (Tailwind number) | | safe-area | no | true | Adds bottom safe area spacer | | root-class | no | '' | Extra classes on root nav |

Navigation items structure:

interface TgNavItem {
  key: string        // Unique identifier
  label: string      // Display text
  icon?: string      // Icon name (optional)
  to?: string        // Route path (optional)
}

Routing guidance

  • Use to (router) for internal navigation to avoid conflicts with the fixed <TgNav>.
  • Reserve href for external links.

🔧 Configuration

Nuxt Configuration

Key settings in nuxt.config.ts:

export default defineNuxtConfig({
  ssr: false,              // SPA mode for Telegram
  srcDir: 'app',           // App source directory
  router: {
    options: {
      // Use history mode so Telegram's #tgWebAppData is not rewritten to a route
      hashMode: false
    }
  },
  modules: [
    '@nuxt/icon',          // Icon support
    '@nuxtjs/tailwindcss', // Tailwind CSS
  ],
  app: {
    head: {
      script: [{
        src: 'https://telegram.org/js/telegram-web-app.js?58',
        defer: true
      }]
    }
  }
})

Telegram WebApp Script

The template automatically includes the Telegram WebApp script. The version can be updated in the Nuxt config.

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests for new features
  5. Submit a pull request

📝 License

MIT License - see LICENSE file for details.

🆘 Support

  • 📖 Documentation: Check this README and inline code comments
  • 🐛 Issues: Report bugs via GitHub Issues
  • 💡 Discussions: Use GitHub Discussions for questions
  • 📚 Telegram Docs: Telegram Mini Apps Documentation

🏗️ Built With

🔗 Links


Made with ❤️ using Nuxt 4