Build Your First Feature
This tutorial walks through the first meaningful customization most developers need: add a new bot command and a matching Mini App screen.
It uses the scaffolded app shape generated by create-teleforge-app.
Goal
Add:
- a new
/ordersbot command - a new
/ordersMini App route - a simple screen that proves the route is wired correctly
1. Update the Manifest
Open teleforge.app.json.
Add a new command entry:
{
"command": "orders",
"description": "Open recent orders",
"handler": "commands/orders"
}
Add a new route entry:
{
"path": "/orders",
"component": "pages/Orders",
"launchModes": ["compact", "fullscreen"]
}
Important: in current Teleforge V1, these strings are conventions and metadata, not magic auto-imports.
They should line up with the files you create:
handler: "commands/orders"->apps/bot/src/commands/orders.tscomponent: "pages/Orders"->apps/web/src/pages/Orders.tsx
teleforge doctor uses the component convention when checking route files, and the manifest remains the clearest place to document intended app structure.
2. Create the Bot Command
Create apps/bot/src/commands/orders.ts:
import type { BotCommandDefinition } from "@teleforgex/bot";
export function createOrdersCommand(miniAppUrl: string): BotCommandDefinition {
return {
command: "orders",
description: "Open recent orders",
async handler(context) {
await context.replyWithWebApp(
"Open the Orders screen in the Mini App.",
"Open Orders",
`${miniAppUrl.replace(/\/$/, "")}/orders`
);
}
};
}
Then register it in apps/bot/src/runtime.ts next to the generated /start command:
import { createOrdersCommand } from "./commands/orders.ts";
runtime.registerCommands([
createStartCommand(config.miniAppUrl),
createOrdersCommand(config.miniAppUrl)
]);
Now the bot knows about /orders.
3. Create the Mini App Screen
Create apps/web/src/pages/Orders.tsx:
export function OrdersPage() {
return (
<div className="stack">
<p className="badge">Route: /orders</p>
<h2>Recent Orders</h2>
<p>This is the first custom screen added to the scaffold.</p>
</div>
);
}
4. Wire the Route in the Web App
SPA Scaffold
If your generated app uses spa mode, update apps/web/src/App.tsx:
- import
OrdersPage - extend the route resolver
- add navigation to
/orders - render
OrdersPagewhen the current route is/orders
The generated SPA app uses a small in-app router built from window.history, so adding a route is just a React state change plus a new page component.
BFF Scaffold
If your generated app uses bff mode, do two things:
- create
apps/web/src/pages/Orders.tsx - create
apps/web/app/orders/page.tsx
apps/web/app/orders/page.tsx should be the same thin wrapper pattern used by the generated page.tsx files:
"use client";
import { OrdersPage } from "../../src/pages/Orders";
export default function Page() {
return <OrdersPage />;
}
5. Test It in the Simulator
Run:
pnpm run dev
Then:
- send
/ordersin the simulator chat - click the
Open Ordersbutton - confirm the Mini App opens at
/orders
If anything is off:
- run
teleforge doctor - check the route path in
teleforge.app.json - check the file names match the manifest conventions exactly
6. Add a Baseline Test
For the bot side, copy the generated /start test pattern and add apps/bot/test/orders.test.ts.
For the web side, copy the generated page render test and add apps/web/test/orders.test.tsx.
That keeps your first feature aligned with the scaffold’s testing model instead of introducing a new test stack.
7. Optional: Send Data Back to the Bot
If your new screen should send a result back to chat:
- use
publishToBot()for a simple payload push - use Teleforge coordination helpers when the feature is a resumable chat -> Mini App -> chat flow
If that is your next step, read Flow Coordination.