こんにちは、「ReactでのLINEログイン機能の実装方法」について、コードと共に以下の3記事で解説します。
LINE認証を活用することで、ユーザーはわずか数タップで会員登録やログインが完了し、離脱率の大幅な低下につながります。特にモバイルファーストの時代、LINEユーザー8,900万人以上という日本の市場特性を考えると、LINEログインは欠かせない機能です。
本記事では、実装の手順を初心者でも理解できるようにサンプルコードをもとにに解説します。※紹介するコードはサンプルとなるため、実際使用しているフレームワーク、言語に合わせて微調整をお願いします。
ReactでLINEログイン機能を実装する準備と概要

今回は、Reactで実装する手順を紹介します。まず全体像です。LINEログインの場合、LINE Developersの設定からフロントエンドの実装まで、いくつかのステップが必要となります。
LINEログインとは?
LINEログインは、OAuth 2.0プロトコルに基づいた認証システムです。ユーザーがLINEアカウントを使って外部サービスにログインできる仕組みで、メールアドレスやパスワードを入力する手間を省略できます。
LINEログインに必要なもの
LINEログインを実装するには、以下の準備が必要です。
ReactにおけるLINEログインの流れ
Reactアプリケーションでは、次のような流れでLINEログインを実装します。
- LIFF SDKの読み込み: HTML内またはReactコンポーネント内でLIFF SDKをロードします。
- LIFF初期化:
liff.init()
メソッドを使って、LIFF IDとともに初期化します。 - ログイン処理: ユーザーがログインボタンをクリックすると、
liff.login()
が実行され、LINEの認証画面に遷移します。 - ユーザー情報取得: ログイン成功後、
liff.getProfile()
などのメソッドでユーザー情報を取得します。 - ページ遷移: ログイン状態に応じて、適切なページコンポーネントを表示します。
これらの基本的な流れを押さえた上で、次に具体的な実装方法を解説します。
ReactでLINEログイン機能を実装する方法【コード付き解説】
ここからは、実際のコードを交えながら、Reactアプリケーションへのログイン機能の実装方法を詳しく解説します。
必要なライブラリのインストールと初期設定
まず、必要なパッケージをインストールします。主にLIFF SDKとReact Routerを使用します。
# npmの場合
npm install @line/liff react-router-dom
# yarnの場合
yarn add @line/liff react-router-dom
次に、HTMLまたはReactコンポーネントでLIFF SDKを読み込みます。2つの方法があります。
方法1: public/index.htmlに直接追加する場合
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>LINE Login with React</title>
<!-- LIFF SDK -->
<script charset="utf-8" src="https://static.line-scdn.net/liff/edge/2/sdk.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
方法2: Reactコンポーネント内で動的に読み込む場合(特にViteを使用している場合)
import { useEffect } from 'react';
import liff from '@line/liff';
function App() {
useEffect(() => {
// LIFF IDは環境変数から読み取るのがベストプラクティス
const liffId = import.meta.env.VITE_LIFF_ID;
// LIFFの初期化
liff
.init({ liffId })
.then(() => {
console.log('LIFF初期化成功');
})
.catch((err) => {
console.error('LIFF初期化エラー', err);
});
}, []);
return (
<div className="App">
{/* アプリケーションの内容 */}
</div>
);
}
export default App;
ReactでLINEログイン機能を実装するフォルダ構成
今回作成したLINEログイン機能を実装したReactアプリケーションの標準的なフォルダ構成を紹介します。
基本的なフォルダ構成
line-login-react-app/
├── .env # 環境変数(LIFF IDなど)
├── .gitignore # Gitの除外ファイル設定
├── package.json # 依存パッケージ管理
├── README.md # プロジェクト説明
├── public/ # 静的ファイル
│ ├── index.html # メインHTMLファイル(LIFF SDK読み込み可)
│ ├── favicon.ico # サイトアイコン
│ └── manifest.json # PWA設定
└── src/ # ソースコード
├── index.js # エントリーポイント
├── index.css # グローバルスタイル
├── App.jsx # メインコンポーネント(ルーティング設定)
├── App.css # Appコンポーネントのスタイル
├── components/ # 再利用可能なコンポーネント
│ ├── Header.jsx # ヘッダーコンポーネント
│ ├── Header.css # ヘッダーのスタイル
│ ├── Footer.jsx # フッターコンポーネント
│ ├── Footer.css # フッターのスタイル
│ ├── LoginComponent.jsx # LINEログインコンポーネント
│ └── MyPage.jsx # マイページコンポーネント
│ └── MyPage.css # マイページのスタイル
├── hooks/ # カスタムフック
│ └── useLiff.js # LIFF関連のロジックをまとめたフック
├── services/ # API通信などのサービス
│ └── lineAuth.js # LINE認証関連の処理
├── utils/ # ユーティリティ関数
│ └── auth.js # 認証関連のヘルパー関数
└── contexts/ # Reactコンテキスト
└── AuthContext.jsx # 認証状態管理用コンテキスト
補足説明
public フォルダ
- index.html: アプリケーションのベースとなるHTMLファイルです。前述のようにここでLIFF SDKをCDNから読み込むこともできます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>LINE Login with React</title>
<!-- 方法1: CDNからLIFF SDKを読み込む場合 -->
<script charset="utf-8" src="https://static.line-scdn.net/liff/edge/2/sdk.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
src フォルダ
このフォルダには、アプリケーションのソースコードが含まれます。
- hooks/useLiff.js: LIFF関連のロジックを一元管理するカスタムフックです。
import { useState, useEffect } from 'react';
import liff from '@line/liff';
export function useLiff(liffId) {
const [isInitialized, setIsInitialized] = useState(false);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [profile, setProfile] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const initialize = async () => {
try {
await liff.init({ liffId });
setIsInitialized(true);
setIsLoggedIn(liff.isLoggedIn());
if (liff.isLoggedIn()) {
const userProfile = await liff.getProfile();
setProfile(userProfile);
}
} catch (e) {
setError(e.message);
console.error('LIFF initialization failed', e);
}
};
initialize();
}, [liffId]);
const login = () => {
if (!isInitialized) return;
liff.login();
};
const logout = () => {
if (!isInitialized) return;
liff.logout();
setIsLoggedIn(false);
setProfile(null);
};
return {
isInitialized,
isLoggedIn,
profile,
error,
login,
logout,
liff
};
}
- services/lineAuth.js: LINEの認証処理をバックエンドと連携するロジックを含みます。
// LINE認証情報をバックエンドと連携するための関数群
export const lineAuthService = {
// バックエンドにLINEのIDトークンを送信して認証
authenticate: async (idToken) => {
try {
const response = await fetch('https://your-api.example.com/auth/line', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ idToken }),
});
if (!response.ok) {
throw new Error('Authentication failed');
}
const data = await response.json();
return data;
} catch (error) {
console.error('LINE Authentication error:', error);
throw error;
}
},
// サーバーから取得したセッショントークンをローカルに保存
saveSession: (sessionToken) => {
localStorage.setItem('session_token', sessionToken);
},
// セッションチェック
checkSession: async () => {
const token = localStorage.getItem('session_token');
if (!token) return false;
try {
const response = await fetch('https://your-api.example.com/auth/verify', {
headers: {
'Authorization': `Bearer ${token}`,
},
});
return response.ok;
} catch (error) {
return false;
}
},
// ログアウト処理
clearSession: () => {
localStorage.removeItem('session_token');
}
};
- contexts/AuthContext.jsx: アプリケーション全体で認証状態を共有するためのコンテキスト。
import { createContext, useContext, useState, useEffect } from 'react';
import { useLiff } from '../hooks/useLiff';
import { lineAuthService } from '../services/lineAuth';
// 認証コンテキストの作成
const AuthContext = createContext(null);
export function AuthProvider({ children, liffId }) {
const {
isInitialized,
isLoggedIn,
profile,
error,
login,
logout,
liff
} = useLiff(liffId);
const [authState, setAuthState] = useState({
isAuthenticated: false,
user: null,
loading: true
});
useEffect(() => {
const checkAuth = async () => {
if (!isInitialized) return;
if (isLoggedIn && profile) {
try {
// バックエンドで認証
const idToken = liff.getIDToken();
const authResult = await lineAuthService.authenticate(idToken);
// セッション情報の保存
lineAuthService.saveSession(authResult.sessionToken);
setAuthState({
isAuthenticated: true,
user: {
...profile,
...authResult.user
},
loading: false
});
} catch (e) {
setAuthState({
isAuthenticated: false,
user: null,
loading: false
});
}
} else {
setAuthState({
isAuthenticated: false,
user: null,
loading: false
});
}
};
checkAuth();
}, [isInitialized, isLoggedIn, profile, liff]);
const handleLogout = () => {
logout();
lineAuthService.clearSession();
setAuthState({
isAuthenticated: false,
user: null,
loading: false
});
};
const value = {
...authState,
login,
logout: handleLogout,
error
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
// カスタムフック
export function useAuth() {
const context = useContext(AuthContext);
if (context === null) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}
App.jsx での利用例
認証コンテキストを使った、アプリケーションのルーティング設定例:
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { AuthProvider, useAuth } from './contexts/AuthContext';
import Header from './components/Header';
import Footer from './components/Footer';
import LoginComponent from './components/LoginComponent';
import MyPage from './components/MyPage';
import './App.css';
// 認証が必要なルートのラッパー
function ProtectedRoute({ children }) {
const { isAuthenticated, loading } = useAuth();
if (loading) {
return <div>Loading...</div>;
}
return isAuthenticated ? children : <Navigate to="/" />;
}
function AppContent() {
return (
<BrowserRouter>
<div className="app-container">
<Header />
<main className="main-content">
<Routes>
<Route path="/" element={<LoginComponent />} />
<Route
path="/mypage"
element={
<ProtectedRoute>
<MyPage />
</ProtectedRoute>
}
/>
</Routes>
</main>
<Footer />
</div>
</BrowserRouter>
);
}
function App() {
// 環境変数からLIFF IDを取得
const liffId = process.env.REACT_APP_LIFF_ID || import.meta.env.VITE_LIFF_ID;
return (
<AuthProvider liffId={liffId}>
<AppContent />
</AuthProvider>
);
}
export default App;
フォルダ構成のポイント
このようなフォルダ構成を採用することで、機能拡張やチーム開発がスムーズになります。プロジェクトの規模や要件に応じて、さらに細かく分割したり、状態管理ライブラリ(ReduxやRecoil)を導入したりすることも検討できます。
続いて、実際のログイン処理を実装していきます。→【コード公開】LIFEアプリ作成手順|LINEログイン機能を実装する方法②