- DefineConfig and Bootstrap
- Presets
- base
- slide
- form
- Parameters
- paywall
- paywall_single
- Parameters
- paywall_row
- Parameters
- Media
- Button Actions
- Theme
- Currency Config
- Localization
- Currency localization
- Custom Presets
You can check their area of responsibility here
The structure of your configuration:
export default defineConfig({
// [Required]. App pages configuration.
// More info in the "Presets" section
pages: [...],
// [Optional]. Theme configuration.
// More information in the "Theme" section
theme: '',
// [Optional]. Localization configuration.
// More information in the "Localization" section
locale: {},
// [Optional]. Currency configuration.
// More information in the "Currency Config" section
currencyConfig: {},
// [Optional]. custom presets configuration
// More info in the "Custom Presets" section
definePresets: {},
});
In the pages
section of your configuration file, you can extend our presets with the following code:
export default defineConfig({
pages: [
{
extends: 'base',
},
{
extends: 'slide',
},
{
extends: 'form',
},
// ...
],
});
You can see all available extensions in the examples below
{
"extends": "base",
"slides": []
}
This is the main preset of your application, representing a Carousel. You can use it without defining an extends keyword. We will automatically resolve this preset only for root elements inside the pages section
export default defineConfig({
pages: [
{
// optional
// extends: 'base',
slides: [],
},
],
});
The only required parameter for this base.preset
is slides
, which takes an array of other presets.
export default defineConfig({
pages: [
{
slides: [
{
extends: 'slide',
// ...
},
{
extends: 'from',
// ...
},
// ...
],
},
],
});
By default, if there is no extends
keyword inside the slides
, the Base
will use slide preset
So, this is valid config for this case:
export default defineConfig({
pages: [
{
slides: [
{
title: 'Title',
// ...
},
// ...
],
},
],
});
{
"extends": "slide"
}
This preset can be used as a standalone page or inside base.preset
export default defineConfig({
pages: [
{
// it's important to use a extends: 'slide' here for a standalone page
extends: 'slide',
},
],
});
Refer to the examples above for base.preset
The examples below are for standalone page usage. The same configuration can be used for a slide inside base.preset.
And Button
will be shown only inside Telegram
Important
All text parameters support HTML inside strings. For example:
title: 'Hello <b>World!</b>'
Please ensure that you are writing safe HTML.
export default defineConfig({
pages: [
{
extends: 'slide',
title: 'Title',
},
],
});
You can configurate some actions by clicking on the MainButton with this parameter. More about this see here
export default defineConfig({
pages: [
{
extends: 'slide',
title: 'Title',
button: 'Button',
},
],
});
export default defineConfig({
pages: [
{
extends: 'slide',
title: 'Title',
description: 'Description',
button: 'Button',
},
],
});
All available media parameters see here
export default defineConfig({
pages: [
{
extends: 'slide',
media: {
type: 'image',
// you can use here src as webp, png, jpg and so on
src: import('./assets/img/base.webp'),
},
title: 'Title',
description: 'Description',
button: 'Button',
},
],
});
With the code above you will get this result:
Keep in mind that all media presets with type="image"
by default will be with aspect-ratio: 1/1
You can write list as array of string. We will add default icon for them
export default defineConfig({
pages: [
{
extends: 'slide',
title: 'Title',
description: 'Description',
list: ['Item 1', 'Item 2', 'Item 3'],
button: 'Button',
},
],
});
Our you can write list as array of objects with parameters:
{
// media is optional. All available media parameters see media section of this page
media?: {},
text: 'string',
}
export default defineConfig({
pages: [
{
extends: 'slide',
title: 'Title',
description: 'Description',
list: [
{
text: 'item 1',
},
{
media: {
type: 'icon',
src: import('./assets/icons/star.svg'),
},
text: 'item 2',
},
],
button: 'Button',
},
],
});
If you are using the slide.preset inside slides in base.preset, you can use the pagination
parameter to show the counter on the page. If it's not inside base.preset, pagination won't be displayed.
export default defineConfig({
pages: [
{
slides: [
{
extends: 'slide',
title: 'Title',
description: 'Description',
pagination: 'count',
list: ['Item 1', 'Item 2', 'Item 3'],
button: 'Button',
},
],
},
],
});
You can set the text alignment of your content using the textAlign
parameter.
Available options: 'left' | 'right' | 'center'
Default: 'left'
Note
This parameter won't affect list and forms
export default defineConfig({
pages: [
{
extends: 'slide',
title: 'Title',
description: 'Description',
textAlign: 'right',
list: ['Item 1', 'Item 2', 'Item 3'],
button: 'Button',
},
],
});
export default defineConfig({
pages: [
{
slides: [
{
extends: 'slide',
title: 'Title',
description: 'Description',
textAlign: 'center',
pagination: 'count',
list: ['Item 1', 'Item 2', 'Item 3'],
button: 'Button',
},
],
},
],
});
You can set shape of your content with help of shape
parameter
Available parameters: 'rounded' | 'stacked' | 'square'
default: 'square'
export default defineConfig({
pages: [
{
extends: 'slide',
title: 'Title',
media: {
type: 'image',
src: import('./assets/img/durov.webp'),
},
description: 'Description',
list: ['Item 1', 'Item 2', 'Item 3'],
button: 'Button',
},
{
extends: 'slide',
title: 'Title',
media: {
type: 'image',
src: import('./assets/img/durov.webp'),
},
description: 'Description',
shape: 'stacked',
list: ['Item 1', 'Item 2', 'Item 3'],
button: 'Button',
},
{
extends: 'slide',
title: 'Title',
media: {
type: 'image',
src: import('./assets/img/durov.webp'),
},
shape: 'rounded',
description: 'Description',
list: ['Item 1', 'Item 2', 'Item 3'],
button: 'Button',
},
],
});
With the code above you will get this result:
You can set a custom background for the content on the slide if want.
This parameter will apply only to the content inside the slide, not the entire page
export default defineConfig({
pages: [
{
extends: 'slide',
title: 'Title',
description: 'Description',
background: '#00ff00',
list: ['Item 1', 'Item 2', 'Item 3'],
button: 'Button',
},
],
});
{
"extends": "form"
}
This preset supports all parameters from the slide and introduces new ones: form
.
You can specify your form with this parameter. Ensure that your IDs are unique
export default defineConfig({
pages: [
{
extends: 'form',
title: 'Title',
description: 'Description',
form: [
{
id: 'id1',
type: 'text',
placeholder: 'Text input',
},
{
id: 'id2',
type: 'number',
placeholder: 'Number input',
},
{
id: 'id3',
type: 'checkbox',
placeholder: 'Checkbox input',
},
],
button: 'Button',
},
],
});
If you are familiar with other input types, you can also use them, such as type='date'.
All native input attributes are available.
export default defineConfig({
pages: [
{
extends: 'form',
title: 'Title',
description: 'Description',
form: [
{
id: 'id1',
type: 'date',
placeholder: 'Input date',
},
],
button: 'Button',
},
],
});
{
"extends": "paywall"
}
The currency can be customized. See these examples.
This preset supports all parameters except button
from the slide and introduces new ones:
export default defineConfig({
pages: [
{
extends: 'paywall',
title: 'Title',
description: 'Description',
products: [
{
id: 'id1',
title: 'Title',
// You can display the price instead of the description using this "trick"
description: '',
price: 10,
},
{
id: 'id2',
title: 'Title 2',
description: 'Description 2',
price: 20,
discount: 'Discount text',
},
],
links: [],
mainButtonText: 'Button',
},
],
});
To display a link for terms of use and other necessary information for subscriptions or payments.
export default defineConfig({
pages: [
{
extends: 'paywall',
title: 'Title',
description: 'Description',
products: [
...
],
links: [
{
text: 'Link 1',
href: 'https://google.com'
},
{
text: 'Link 2',
href: 'https://google.com'
}
],
mainButtonText: 'Button',
},
],
});
By default will be with "Continue" text
You can display the price inside the main button like this:
export default defineConfig({
pages: [
{
extends: 'paywall',
title: 'Title',
description: 'Description',
products: [
...
],
links: [],
mainButtonText: 'Subscribe for {price}',
},
],
});
By default will be show our predefinned popup
By default: 'telegram'
You can specify this parameter to indicate your preference for displaying only web popups
export default defineConfig({
pages: [
{
extends: 'paywall',
title: 'Title',
description: 'Description',
products: [
...
],
links: [],
popup: {
type: 'web'
},
},
],
});
By default: 'Choose the payment method'
export default defineConfig({
pages: [
{
extends: 'paywall',
title: 'Title',
description: 'Description',
products: [
...
],
links: [],
popup: {
type: 'web',
title: 'Are you happy?'
},
},
],
});
export default defineConfig({
pages: [
{
extends: 'paywall',
title: 'Title',
description: 'Description',
products: [
...
],
links: [],
popup: {
type: 'web',
title: 'Are you happy?',
message: 'Tell me'
},
},
],
});
By default: ['telegram_payments', 'wallet_pay']
export default defineConfig({
pages: [
{
extends: 'paywall',
title: 'Title',
description: 'Description',
products: [
...
],
links: [],
popup: {
type: 'web',
title: 'Are you happy?',
buttons: [
{
id: 'yes',
text: 'Yes!',
type: 'default'
},
{
id: 'no',
text: 'No!',
type: 'destructive'
}
]
},
},
],
});
If the popup mode is type='web'
you can specify the media parameter inside buttons:
All available parameters for media see here
export default defineConfig({
pages: [
{
extends: 'paywall',
title: 'Title',
description: 'Description',
products: [
...
],
links: [],
popup: {
type: 'web',
title: 'Are you happy?',
buttons: [
{
id: 'yes',
text: 'Yes!',
type: 'default',
media: {
type: 'emodji',
src: '💳'
}
},
{
id: 'no',
text: 'No!',
type: 'destructive',
media: {
type: 'emodji',
src: '💳'
}
}
]
},
},
],
});
{
"extends": "paywall_single"
}
This preset supports all parameters (except products
) from the paywall and add new one:
export default defineConfig({
pages: [
{
extends: 'paywall_single',
title: 'Title',
description: 'Description',
product: {
id: 'id',
title: 'Product Title',
price: 99,
description: 'Product Description',
},
links: [],
},
],
});
You can also include some media for your product
export default defineConfig({
pages: [
{
extends: 'paywall_single',
title: 'Title',
description: 'Description',
product: {
id: 'id',
title: 'Product Title',
media: {
type: 'icon',
src: import('./assets/icons/star.svg'),
size: 40,
},
price: 99,
description: 'Product Description',
},
links: [],
},
],
});
{
"extends": "paywall_row"
}
This preset supports all parameters from the paywall but overrides products:
export default defineConfig({
pages: [
{
extends: 'paywall_row',
title: 'Title',
description: 'Description',
products: [
{
id: 'id1',
price: 4.99,
title: '4<br />credits',
description: 'Perfect to<br />start with',
},
{
id: 'id2',
price: 8.99,
title: '20<br />credits',
description: 'Best value<br />offer',
bestText: 'Best Choice',
},
{
id: 'id3',
price: 19.99,
title: '100<br />credits',
description: 'For true<br />enthusiasts',
},
],
links: [],
},
],
});
If you have a lot of products or only two, don't worry this component will take care of how to display them
export default defineConfig({
pages: [
{
extends: 'paywall_row',
title: 'Title',
description: 'Description',
products: [
{
id: 'id1',
// ...
},
{
id: 'id2',
// ...
},
{
id: 'id3',
// ...
},
{
id: 'id4',
// ...
},
{
id: 'id5',
// ...
},
{
id: 'id6',
// ...
},
],
links: [],
},
],
});
Media supports 5 types of content:
{
media: {
type: 'image',
src: import('path/to/image.png'),
// Optional.
webp?: import('path/to/image.webp'),
// Optional. If you aren't happy with our styles you can override them with this props
style?: 'margin: ...; position: absolute; ...'
}
}
Styles by default:
.image {
aspect-ratio: 1/1;
}
{
media: {
type: 'image',
src: import('path/to/sticker.tgs'),
// Optional.
// size paramter of the sticker
// if it's a number, like `size: 20` it will translate to:
// {
// width: 20px;
// height: 20px;
// }
size?: number | [number, number],
// if it's a [number, number], like `size: [20, 40]` it will translate to:
// {
// width: 20px;
// height: 40px;
// },
// Optional
style?: 'margin: ...; position: absolute; ...'
}
}
Styles by default:
.sticker {
aspect-ratio: 1/1;
max-height: 40vw;
}
{
media: {
type: 'video',
src: import('path/to/video.mp4'),
// Optional.
// For a better user experience, you need to add this
poster?: import('path/to/poster.png'),
// Optional
style?: 'margin: ...; position: absolute; ...'
}
}
Styles by default:
.video {
aspect-ratio: 16/9;
}
{
media: {
type: 'icon',
src: import('path/to/icon.svg'),
// Optional.
size?: number | [number, number],
// Optional
style?: 'margin: ...; position: absolute; ...'
}
}
Styles by default:
.icon {
width: 1.5em;
height: 1.5em;
}
{
media: {
type: 'emodji',
src: '💳',
// Optional
size?: number | [number, number],
// Optional
style?: 'margin: ...; position: absolute; ...'
}
}
Styles by default:
.emodji {
width: 1.625em;
height: 1.625em;
}
You can specify button action behavior inside slide.preset. It will be shown as text with the default behavior of navigating to the next slide if possible.
Note
You can't override button behavior for the paywalls
export default defineConfig({
pages: [
{
extends: 'slide',
button: 'Text',
},
],
});
Alternatively, you can navigate to a different page. However, you need to specify the name of that page within your configuration. Here's how you can do it:
export default defineConfig({
pages: [
{
extends: 'slide',
button: {
content: 'Go to paywall',
to: '/paywall', // this
},
},
{
extends: 'paywall',
path: '/paywall', // and this will be connected
},
],
});
Note
If you need more actions, let us know, and we will easily add them
You can specify what theme do you want to use inside your application
By default it will inherit Telegram.colorScheme
export default defineConfig({
theme: 'light' | 'dark' | 'auto',
});
You can specify what currency do you want to use inside your application
These are the parameters you can change and their default values:
const config = {
// currency symbol alignment
// default: 'left'
align?: 'left' | 'right';
// currency symbol
// default: 'USD'
currency?: CurrencyVariants;
// separator for decimal 1.00 or 1,00 as you wish
// default '.'
decimalSeparator?: string;
// separator for thousand 1_000_000 or 1x000x000
// default ' '
thousandSeparator?: string;
}
It's support localization
export default defineConfig({
currencyConfig: {
currency: 'AUD',
align: 'right',
},
});
For localization you need to define json
files with your tokens and localized values.
For example:
// ru.json
{
"_hello": "Привет!"
}
// en.json
{
"_hello": "Hello!"
}
It's not necessary to use _
at the beginning of each token; it's just for illustration purposes to make it easier to determine which value is being localized
export default defineConfig({
locale: {
fallback: 'en',
ru: import('./locales/ru.json'),
en: import('./locales/en.json'),
},
pages: [
{
extends: 'slide',
// this will be automaticaly translated
title: '_hello',
},
],
});
Note
Our i18n logic is extremely simple. You can use tokens like slide1.product.title
.
To correctly resolve the token value, your config should contain this:
// locales/en.json
{
"slide1": {
"product": {
"title": "Product Title"
}
}
}
Do not try to trick it with values like: we haven't tested it yet :(
// locales/en.json
{
"slide1.product.title": "Product Title"
}
If your application supports the user's language, that language will be downloaded, and then the application will call the Telegram.ready()
method to indicate that the app is ready for use.
If the user's language is not supported, the application will switch to the fallback language and download the locale for it.
If you haven't defined a locale, we will call the Telegram.ready()
method right after the main page of the application is mounted.
You can also use localization within the currency configuration:
// ru.json
{
"_currency": "RUB",
"_price": 1000,
"_align": "right",
"_decimal": ",",
"_thousands": "_"
}
// en.json
{
"_currency": "USD",
"_price": 10,
"_align": "left",
"_decimal": ".",
"_thousands": " "
}
export default defineConfig({
locale: {
fallback: 'en',
ru: import('./locales/ru.json'),
en: import('./locales/en.json'),
},
currencyConfig: {
currency: '_currency',
align: '_align',
decimalSeparator: '_decimal',
thousandSeparator: '_thousands',
},
pages: [
{
extends: 'paywall',
products: [
{
price: '_price',
// ...
},
],
// ...
},
],
});
Note
Try to avoid using reserved preset names above. You can certainly do it to override ours, but you should understand what you're doing
You can define custom presets if you want to using the following code:
// this will be inside intial bundle
import CustomPreset from './CustomPreset.vue';
// this will be loaded asynchronously when such preset will be shown
const AsyncCustomPreset = defineAsyncComponent(
() => import('./CustomPreset.vue')
);
export default defineConfig({
definePresets: {
your_custom_name: CustomPreset,
async_your_custom_name: AsyncCustomPreset,
},
// and use them as:
pages: [
{
extends: 'your_custom_name',
// extends: 'async_your_custom_name',
// some props
},
],
});