This is a starter project for building MVP (minimal viable product) for Thai users.
The starter provides you:
- Authentication flow via Line login and store user to Fibase auth.
- React Contect and hook to get the status and data from the authentication flow.
- The user data is kept in Firestore database with minimal secure rules (only the user can access their own data).
- Ready-to-use static site generation with Next.js that deploys on Firebase hosting via Github actions.
- Configured TypeScript
You can try the demo here: Jun MVP starter demo
Before you begin, you'll need to set up the following accounts and services:
- Go to GitHub.com and click "Sign up"
- Enter your email, create a password, and choose a username
- Verify your account through the email you receive
- Complete any additional verification steps if prompted
- Go to Firebase Console
- Click "Add project" and enter a project name
- Choose whether to enable Google Analytics (optional)
- Click "Create project" and wait for setup to complete
- On the project overview page, click the web icon (</>) to add a web app
- Enter a nickname for your app (e.g., "My MVP Web")
- Check the "Also set up Firebase Hosting" option
- Click "Register app" and continue through the setup
- Enable Firestore Database:
- In the left sidebar, click "Firestore Database"
- Click "Create database"
- Keep the default name and choose location
asia-southeast1 (Singapore)
. - Start in production mode
-
Create a LINE Developer account:
- Go to LINE Developers Console
- Sign in with your LINE account or create one if needed
- Create a new provider if you don't have one (click "Create" next to Providers)
- Enter a provider name and click "Create"
-
Create a new channel:
- At the selected provider, click "Create a new channel"
- Choose "LINE Login"
- Fill in required information and click "Create" at the end of the form
-
Set up LIFF app:
- In your channel, go to the LIFF tab
- Click "Add" to create a new LIFF app
- Enter the following details:
- LIFF app name: Your app name
- Size: Full (or choose another size based on your needs)
- Endpoint URL:
https://localhost:4416
- Scopes: Check "profile" and "openid"
- Click "Add"
- Note your LIFF ID for later use in environment variables
Please follow the Prerequisite first.
After you clone the project to your local machine, do the following:
There are 2 places to configure the env variables:
.env.local
for Next.js (frontend)functions/.secret.local
for Firebase Cloud Functions (backend)
the env files are for local only, they are ignored from git.
.env.local
Duplicate file .env.example
and rename to .env.local
, then update the content of the file with the following variables:
*_FIREBASE_*
: You can find these variables from Firebase console.NEXT_PUBLIC_LINE_LIFF_ID
: access the LINE Developers Console, select your provider and channel, then locate the LIFF ID under Basic settings after adding the LIFF app to your channel.
functions/.secret.local
Duplicate file functions/.secret.example
and rename to .secret.local
, then update the content of the file with the following variables:
LINE_CHANNEL_ID
: log into the LINE Developers Console, navigate to your Messaging API channel, and then go to the "Basic settings" tab; your Channel ID will be displayed there.
- Install dependencies
npm install
- Run development
Next.js app:
npm run dev
Then open another terminal for the local backend:
npm run emulators
Both support hot-reloading
- Frontend is running on port
https://localhost:4416
- Backend (GUI) is running on port
http://localhost:4000
You just need to configure the production env to work with Github actions and deployment to Firebase hosting.
Follow the following configuration before you commit/push the code to the main
branch.
Create a new channel for production (it's the same steps you did).
You can add the "Endpoint URL" later. You will know the url after the first production deployment by going to Firebase console hosting page.
To add a GitHub Actions secret, navigate to your repository's "Settings," then "Secrets and variables", click "Actions," and finally "New repository secret" to enter the secret name and value.
Use the same variables as in .env.local
. Fore Firebase variables, the values can be the same as the .env.local
file.
However, it's recommended to create a different Line channel from the one you use for local development.
Firebase token for frontend deployment
you can get the value by running firebase login:ci
and use the value returned from the command.
Add the secret with name FIREBASE_TOKEN
and use the value from the command.
Service account for backend deployment
In the Firebase console, open Settings > Service Accounts. Click Generate New Private Key, then confirm by clicking Generate Key.
Save the file to the root of the project (it will be ignored from git, do not change the file name).
Add the secret with name FIREBASE_SERVICE_ACCOUNT
and use the value the service account file (copy the JSON content and paste to the value input).
Secrets for cloud functions
To add Firebase secrets through the CLI, navigate to your project directory, run:
firebase functions:secrets:set LINE_CHANNEL_ID
If you haven't logged in, run
firebase login
first.
Then enter the secret value (the Line channel ID for production). You can check the added value by running firebase functions:secrets:access LINE_CHANNEL_ID
.
- Copy the rules from
firestore.rules
file - Open Firebase console > Firestore > Rules tab
- Paste the existing rules and save
The project follows a standard Next.js application structure with additional Firebase configuration:
├── src/ # Source code
│ ├── app/ # Next.js App Router
│ │ ├── page.tsx # Main dashboard page
│ │ ├── layout.tsx # Root layout component
│ │ └── globals.css # Global styles
│ ├── components/ # React components
│ │ ├── ui/ # UI components (shadcn/ui)
│ │ └── WelcomeDialog.tsx # User welcome dialog
│ ├── contexts/ # React contexts
│ │ └── AuthContext.tsx # Authentication context provider
│ ├── lib/ # Utility libraries
│ │ ├── firebase.ts # Firebase configuration and utilities
│ │ └── utils.ts # General utility functions
│ └── server-types.d.ts # TypeScript definitions for server types
├── functions/ # Firebase Cloud Functions
│ ├── src/ # Cloud Functions source code
│ │ └── index.ts # Main functions entry point (LINE auth)
│ └── lib/ # Functions utility libraries
├── public/ # Static assets
├── .github/workflows/ # GitHub Actions workflows
│ ├── web-release.yml # Production deployment workflow
│ ├── web-pr-preview.yml # PR preview deployment workflow
│ └── deploy-functions.yml # Functions deployment workflow
├── .env.example # Example environment variables
├── .env.local # Local environment variables (git-ignored)
├── firebase.json # Firebase configuration
├── firestore.rules # Firestore security rules
└── next.config.ts # Next.js configuration
- src/app: Contains the Next.js application using the App Router
- src/components: Reusable React components
- src/contexts/AuthContext.tsx: Manages authentication state and user data
- src/lib/firebase.ts: Firebase configuration and utility functions
- functions/src/index.ts: Cloud Functions for LINE authentication
- .github/workflows: CI/CD pipelines for automated deployment
- firestore.rules: Security rules for Firestore database
The authentication flow integrates LINE Login with Firebase Authentication:
-
LIFF Initialization: The app initializes the LINE LIFF SDK and checks if the user is already logged in.
-
LINE Login: When a user clicks the login button, they're redirected to LINE's login page. After successful login, they're redirected back with authentication tokens.
-
Firebase Integration: The app sends the LINE ID token to a Firebase Cloud Function, which verifies it and creates a Firebase custom token. It also creates or updates the user's document in Firestore.
-
User Session: The app signs in to Firebase using the custom token and loads the user profile data from Firestore. Authentication state is maintained using React context.
-
Logout Process: When logging out, the app signs out from both Firebase and LINE, and the UI is updated to show the login button again.
A React context provider that manages authentication state and user data.
// Wrap your app with AuthProvider
import { AuthProvider } from "@/contexts/AuthContext";
function App() {
return (
<AuthProvider>
<YourApp />
</AuthProvider>
);
}
A React hook that provides access to authentication state and functions.
import { useAuth } from "@/contexts/AuthContext";
function YourComponent() {
const {
// Authentication status
authStatus, // Current auth status: "idle" | "authenticating" | "fetchingProfile" | "error"
isNewUser, // Whether the user is new (first login)
shouldShowLogin, // Whether to show the login button
// User data
authUser, // Firebase Auth user object
userProfile, // User profile data from Firestore
lineProfile, // LINE profile data
// Authentication actions
login, // Function to trigger LINE login
logout, // Function to sign out from both Firebase and LINE
} = useAuth();
// Use these values and functions in your component
}
Fatest POC with no cost!
- Line Login because it's a very popular chat app that most Thai people use. It has other powerful features when you scale the app.
- Next.js offers a simple config (just one flag) to generate static pages.
- I prefer SSG over SSR because it's simpler for MVP which is more flexible to pick CDN and cost effective.
- Firebase hosting over Vercel because Vercel will charge you $25 for each new team members.
- Firebase custom token because it's the only way to create users from Line with free tier applied. Another way is to use Open ID connect which simplifies the auth flow because you no longer need the cloud function but it might cost a lot of money, see Tier 2 pricing.