← Back to Blog
LINE LIFFThailandDevelopment

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 →