Building LINE LIFF Apps: A Developer's Guide for Thailand
1 May 2026 · by Yunmin Shin
What Is LINE LIFF and Why Does It Matter in Thailand?
LINE is the dominant messaging platform in Thailand, with over 50 million users. LINE LIFF (LINE Front-end Framework) lets you embed a full web application inside the LINE app. Users can access your app without leaving LINE, without creating a new account, and without entering payment details if you integrate LINE Pay.
For Bangkok businesses already using LINE for customer communication — which is most of them — LIFF is a natural way to deliver rich, app-like experiences inside the channel where customers already spend their time. A clinic that currently takes bookings by having a staff member manually read messages in a LINE OA inbox and reply with available slots is a textbook case: replace that with a LIFF mini-app opened directly from the clinic's LINE Official Account, and the same booking flow that used to require a human on both ends becomes self-service, available at midnight, and doesn't depend on whoever is monitoring the chat that day.
How Do You Set Up a LIFF App?
Step 1: Create a LINE Login channel. Go to the LINE Developers Console, create a provider, and add a LINE Login channel. Under the channel settings, create a LIFF app and set the endpoint URL to your web app's URL (e.g., https://yourapp.com).
Step 2: Note your LIFF ID. Each LIFF app has a unique ID in the format 1234567890-AbCdEfGh. You will need this in your code.
Step 3: Install the LIFF SDK:
npm install @line/liff
Step 4: Initialize LIFF in your app:
import liff from "@line/liff";
await liff.init({ liffId: process.env.NEXT_PUBLIC_LIFF_ID });
In Next.js, call liff.init() in a useEffect hook inside a client component, since LIFF requires access to browser APIs. Wrap the whole booking flow in a loading state until liff.isLoggedIn() resolves — LIFF's initialization is asynchronous and can take a noticeable moment on a mid-range Android device inside LINE's in-app browser, and an unstyled flash of "please log in" before LIFF finishes checking reads as broken to a first-time user.
How Do You Access User Identity?
Once initialized, LIFF provides the logged-in LINE user's profile:
const profile = await liff.getProfile();
// profile.userId, profile.displayName, profile.pictureUrl
You can use profile.userId as a stable identifier to look up or create users in your database. This eliminates the need for a traditional login form — the user is already authenticated via LINE. In a clinic booking schema, this maps directly onto a patients.line_user_id column: the first time someone opens the LIFF app, you create a patient record keyed on their LINE user ID; every visit after that, you already know who they are without asking them to type anything.
To verify the user's identity server-side, call liff.getIDToken() and send the token to your backend, where you verify it using LINE's public keys. This prevents spoofed userId values. Never trust a userId sent as a plain request parameter from the client — always verify the ID token server-side before writing anything to the database under that identity, especially for something like a booking system where a spoofed ID could cancel or view another patient's appointment.
How Do You Build a Booking Flow Inside LIFF?
A typical clinic or salon LIFF booking app follows a simple flow: treatment selection, branch and staff selection, available time slots pulled from your appointments table filtered by branch and date, and a confirmation screen that writes the new appointment and immediately triggers a LINE push message confirming the slot. Because the user is already identified via profile.userId, the whole flow can skip account creation entirely:
async function bookAppointment(treatmentId: string, slot: string) {
const profile = await liff.getProfile();
const idToken = liff.getIDToken();
await fetch("/api/appointments", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ idToken, treatmentId, slot }),
});
}
The server verifies idToken, resolves it to a patients row (creating one if it's a first-time LINE user), and inserts the appointment. Send the confirmation as a LINE push message rather than relying on the user to notice it in the LIFF UI — most users close the LIFF window immediately after booking and expect the confirmation to show up in the chat, the same way a human staff reply would have.
How Do You Share Content from LIFF?
LIFF provides a shareTargetPicker API that lets users share messages, images, or rich content to their LINE chats and groups. This is a powerful viral mechanism for Bangkok marketing campaigns:
await liff.shareTargetPicker([
{ type: "text", text: "Check out this promotion from [Brand]!" }
]);
Combining this with a referral system — where each shared link includes a unique code — creates a trackable word-of-mouth marketing channel native to LINE. For a clinic, this is often more effective than paid social ads: a referral from a friend's LINE share carries far more trust than an Instagram promotion, particularly for treatments where the customer is judging results on someone they actually know.
How Does a Rich Menu Fit into This?
The LINE Rich Menu — the persistent image-based menu pinned to the bottom of a chat with your LINE OA — is usually the entry point into your LIFF app, not the LIFF app itself. Design it with clear, tappable sections ("Book Now", "Our Treatments", "Contact Staff") where "Book Now" deep-links directly into your LIFF booking flow via a liff.state parameter that can route the user straight to, say, a specific branch's calendar rather than a generic landing screen. Keep the rich menu to a handful of large tap targets — it's rendered small on a typical Android screen, and cramming in eight tiny icons guarantees mis-taps.
What Are the LIFF Limitations?
LIFF apps run inside LINE's in-app browser, which has some restrictions: no push notification APIs (use LINE's server-side Messaging API to push messages instead, as shown above), limited camera access, and occasional inconsistencies between iOS and Android LINE versions — particularly around viewport height when the LINE chat header is present versus hidden. Test your LIFF app on both platforms before launch, and specifically test what happens when a user's browser back gesture or LINE's own close button interrupts a multi-step flow like a booking form; state should be resumable, not lost.
For payments, integrate LINE Pay rather than card payments where possible — LINE Pay is deeply trusted by Thai consumers and the conversion rate is significantly higher than asking a user to type a full card number inside an in-app browser on their phone.
Ready to Build Something Fast?
Get a free quote. We reply within 24 hours.
Ready to build something fast and scalable?
Get a free project quote. We reply within 24 hours.
Get a Free Quote →