Overview
In this article, I explain how to develop a web page by using Line Front-end Framework (LIFF) SDK and enrich your bot, as well as how to use CLI too for LIFF.
I re-use some code from line-liff-starter repository.
The final version is hosted at here
LIFF application
It's a just another html page with javascript with LIFF SDK, which is registered by using LIFF API to generate unique URL to be used in LINE app.
Prerequisites
- Basic understanding of LINE Bot
- Visual Studio Code
- Azure Subscription
- Azure CLI
- git
- LIFF CLI
※ Even though I use Microsoft Azure to host my page, you can host it anywhere.
Prepare the environment first
Create "Channel" in LINE Developer Portal
Create Messaging API channel in LINE Developer portal, and get channel and token keys.
Please copy and keep "Your user ID" too.
Create WebApps in Azure Portal
You can use Azure Portal to create a web app, but I use CLI this time for fun.
1. Install、Azure CLI, or you can also use "Cloud Shell" on Azure Portal.
2. If you use Cloud Shall, skip to step 4. Otherwise follow the steps to login.
az login
3. Follow the instruction on the screen to complete logging in.
4. Make sure you use right Azure Subscription in case you have some.
az account list
az account set -s 'subscription id'
az account show
5. Create resource group. I created "lineliffsample" resource group in East Japan.
az group create -n lineliffsample -l japaneast
6. Next, create a application service plan and a WebApps to host LIFF application.
az appservice plan create -n lineliffappplan -g lineliffsample --sku F1
az webapp create -n <unique name for WebApps> -g lineliffsample -p lineliffappplan
7. Configure the WebApps to enable git deploy.
az webapp deployment user set --user-name <username> --password <password>
az webapp deployment source config-local-git -n <your webapp name> -g lineliffsample
Use LIFF CLI with existing page
You can actually register any page as LIFF. In this section, I explan how to use CLI tool to register a page as LIFF.
1. Install liff (LIFF CLI tool).
npm install -g liff
2. Run liff init to initialize the tool.
liff init <Channel Access Token>
3. Register LIFF application. I use "tall" here but you can try "full" and "compact" as well.
liff add https://linecorp.com/en/ tall
4. Get LIFF ID and Accessible URL.
5. Now, send the address by using "liff send" command or you can even send it via LINE to your friend. It works outside of the bot, too.
liff send <LIFF ID> <User ID>
Develop LIFF application
In this section, I explain how to develop LIFF application with LIFF SDK.
Create a project
1. Create MyFirstLiff folder and open it via Visual Studio Code.
mkdir MyFirstLiff
cd MyFirstLiff
code .
2. Create index.html.
3. Copy and paste following code to index.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My First LIFF</title>
</head>
<body>
<div id="liffdata">
<h2>LIFF Data</h2>
<table border="1">
<tr>
<th>language</th>
<td id="languagefield"></td>
</tr>
<tr>
<th>context.viewType</th>
<td id="viewtypefield"></td>
</tr>
<tr>
<th>context.userId</th>
<td id="useridfield"></td>
</tr>
<tr>
<th>context.utouId</th>
<td id="utouidfield"></td>
</tr>
<tr>
<th>context.roomId</th>
<td id="roomidfield"></td>
</tr>
<tr>
<th>context.groupId</th>
<td id="groupidfield"></td>
</tr>
</table>
</div>
<!-- Load LIFF SDK -->
<script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
<script src="liff.js"></script>
</body>
</html>
4. Add liff.js in the same directory and paste the followig code.
window.onload = function (e) {
// initialize and get basic information
// https://developers.line.me/en/reference/liff/#initialize-liff-app
liff.init(function (data) {
initializeApp(data);
});
};
function initializeApp(data) {
document.getElementById('languagefield').textContent = data.language;
document.getElementById('viewtypefield').textContent = data.context.viewType;
document.getElementById('useridfield').textContent = data.context.userId;
document.getElementById('utouidfield').textContent = data.context.utouId;
document.getElementById('roomidfield').textContent = data.context.roomId;
document.getElementById('groupidfield').textContent = data.context.groupId;
}
5. Save all.
Deploy LIFF Application.
1. Initialize git. Open the "Integrated terminal" and run git init.
git init
2. Add remote which you obtain while creating WebApps above.
git remote add origin <remote git address of your WebApps>
3. Add all and commit.
git add .
git commit -m "initial"
4. Push to deploy.
git push -u origin master
5. Use LIFF CLI to register.
liff add <WebApps address> tall
6. Now you can use the LIFF address to test. Make sure you get user ID and language of yourself.
Get user profile
1. Replace following code to index.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My First LIFF</title>
</head>
<body>
<div id="profileinfo">
<h2>Profile</h2>
<div id="profilepicturediv">
</div>
<table border="1">
<tr>
<th>userId</th>
<td id="useridprofilefield"></td>
</tr>
<tr>
<th>displayName</th>
<td id="displaynamefield"></td>
</tr>
<tr>
<th>statusMessage</th>
<td id="statusmessagefield"></td>
</tr>
</table>
</div>
<div id="liffdata">
<h2>LIFF Data</h2>
<table border="1">
<tr>
<th>language</th>
<td id="languagefield"></td>
</tr>
<tr>
<th>context.viewType</th>
<td id="viewtypefield"></td>
</tr>
<tr>
<th>context.userId</th>
<td id="useridfield"></td>
</tr>
<tr>
<th>context.utouId</th>
<td id="utouidfield"></td>
</tr>
<tr>
<th>context.roomId</th>
<td id="roomidfield"></td>
</tr>
<tr>
<th>context.groupId</th>
<td id="groupidfield"></td>
</tr>
</table>
</div>
<!-- Load LIFF SDK -->
<script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
<script src="liff.js"></script>
</body>
</html>
2. Replace code in liff.js.
window.onload = function (e) {
// initialize and get basic information
// https://developers.line.me/en/reference/liff/#initialize-liff-app
liff.init(function (data) {
getProfile();
initializeApp(data);
});
};
// Get profile and display
function getProfile(){
// https://developers.line.me/en/reference/liff/#liffgetprofile()
liff.getProfile().then(function (profile) {
document.getElementById('useridprofilefield').textContent = profile.userId;
document.getElementById('displaynamefield').textContent = profile.displayName;
var profilePictureDiv = document.getElementById('profilepicturediv');
if (profilePictureDiv.firstElementChild) {
profilePictureDiv.removeChild(profilePictureDiv.firstElementChild);
}
var img = document.createElement('img');
img.src = profile.pictureUrl;
img.alt = "Profile Picture";
img.width = 200;
profilePictureDiv.appendChild(img);
document.getElementById('statusmessagefield').textContent = profile.statusMessage;
}).catch(function (error) {
window.alert("Error getting profile: " + error);
});
}
function initializeApp(data) {
document.getElementById('languagefield').textContent = data.language;
document.getElementById('viewtypefield').textContent = data.context.viewType;
document.getElementById('useridfield').textContent = data.context.userId;
document.getElementById('utouidfield').textContent = data.context.utouId;
document.getElementById('roomidfield').textContent = data.context.roomId;
document.getElementById('groupidfield').textContent = data.context.groupId;
}
3. push the change.
git commit -am "add profile"
git push origin master
4. As LIFF application address didn't change at all, simply reopen the page to confirm the behavior.
Send message from LIFF
You can send messages to LINE bot from LIFF application. This is great feature to integrate the page into bot.
See detail at liff.sendMessages()
1. Replcae the code for index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My First LIFF</title>
</head>
<body>
<button id="sendmessagebutton">Send Message</button>
<div id="profileinfo">
<h2>Profile</h2>
<div id="profilepicturediv">
</div>
<table border="1">
<tr>
<th>userId</th>
<td id="useridprofilefield"></td>
</tr>
<tr>
<th>displayName</th>
<td id="displaynamefield"></td>
</tr>
<tr>
<th>statusMessage</th>
<td id="statusmessagefield"></td>
</tr>
</table>
</div>
<div id="liffdata">
<h2>LIFF Data</h2>
<table border="1">
<tr>
<th>language</th>
<td id="languagefield"></td>
</tr>
<tr>
<th>context.viewType</th>
<td id="viewtypefield"></td>
</tr>
<tr>
<th>context.userId</th>
<td id="useridfield"></td>
</tr>
<tr>
<th>context.utouId</th>
<td id="utouidfield"></td>
</tr>
<tr>
<th>context.roomId</th>
<td id="roomidfield"></td>
</tr>
<tr>
<th>context.groupId</th>
<td id="groupidfield"></td>
</tr>
</table>
</div>
<!-- Load LIFF SDK -->
<script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
<script src="liff.js"></script>
</body>
</html>
2. Replace code of liff.js
window.onload = function (e) {
// initialize and get basic information
// https://developers.line.me/en/reference/liff/#initialize-liff-app
liff.init(function (data) {
getProfile();
initializeApp(data);
});
// Send message
document.getElementById('sendmessagebutton').addEventListener('click', function () {
// https://developers.line.me/en/reference/liff/#liffsendmessages()
liff.sendMessages([{
type: 'text',
text: "Send text message"
}, {
type: 'sticker',
packageId: '2',
stickerId: '144'
}]).then(function () {
window.alert("Sent");
}).catch(function (error) {
window.alert("Error sending message: " + error);
});
});
};
// Get profile and display
function getProfile(){
// https://developers.line.me/en/reference/liff/#liffgetprofile()
liff.getProfile().then(function (profile) {
document.getElementById('useridprofilefield').textContent = profile.userId;
document.getElementById('displaynamefield').textContent = profile.displayName;
var profilePictureDiv = document.getElementById('profilepicturediv');
if (profilePictureDiv.firstElementChild) {
profilePictureDiv.removeChild(profilePictureDiv.firstElementChild);
}
var img = document.createElement('img');
img.src = profile.pictureUrl;
img.alt = "Profile Picture";
img.width = 200;
profilePictureDiv.appendChild(img);
document.getElementById('statusmessagefield').textContent = profile.statusMessage;
}).catch(function (error) {
window.alert("Error getting profile: " + error);
});
}
function initializeApp(data) {
document.getElementById('languagefield').textContent = data.language;
document.getElementById('viewtypefield').textContent = data.context.viewType;
document.getElementById('useridfield').textContent = data.context.userId;
document.getElementById('utouidfield').textContent = data.context.utouId;
document.getElementById('roomidfield').textContent = data.context.roomId;
document.getElementById('groupidfield').textContent = data.context.groupId;
}
3. Push again.
git commit -am "add send message"
git push origin master
4. Use same LIFF address to confirm the behavior.
Open new window from LIFF
You can open URL inside LINE or outside of LINE from LIFF.
1. Replace code of index.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My First LIFF</title>
</head>
<body>
<button id="openwindowbutton">Open the window in LINE</button>
<button id="openwindowexternalbutton">Open the window outside of LINE</button>
<button id="sendmessagebutton">Send Message</button>
<div id="profileinfo">
<h2>Profile</h2>
<div id="profilepicturediv">
</div>
<table border="1">
<tr>
<th>userId</th>
<td id="useridprofilefield"></td>
</tr>
<tr>
<th>displayName</th>
<td id="displaynamefield"></td>
</tr>
<tr>
<th>statusMessage</th>
<td id="statusmessagefield"></td>
</tr>
</table>
</div>
<div id="liffdata">
<h2>LIFF Data</h2>
<table border="1">
<tr>
<th>language</th>
<td id="languagefield"></td>
</tr>
<tr>
<th>context.viewType</th>
<td id="viewtypefield"></td>
</tr>
<tr>
<th>context.userId</th>
<td id="useridfield"></td>
</tr>
<tr>
<th>context.utouId</th>
<td id="utouidfield"></td>
</tr>
<tr>
<th>context.roomId</th>
<td id="roomidfield"></td>
</tr>
<tr>
<th>context.groupId</th>
<td id="groupidfield"></td>
</tr>
</table>
</div>
<!-- Load LIFF SDK -->
<script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
<script src="liff.js"></script>
</body>
</html>
2. Replace code of liff.js
window.onload = function (e) {
// initialize and get basic information
// https://developers.line.me/en/reference/liff/#initialize-liff-app
liff.init(function (data) {
getProfile();
initializeApp(data);
});
// Open window
// https://developers.line.me/en/reference/liff/#liffopenwindow()
document.getElementById('openwindowbutton').addEventListener('click', function () {
liff.openWindow({
url: 'https://line.me'
});
});
document.getElementById('openwindowexternalbutton').addEventListener('click', function () {
liff.openWindow({
url: 'https://line.me',
external: true
});
});
// Send message
document.getElementById('sendmessagebutton').addEventListener('click', function () {
// https://developers.line.me/en/reference/liff/#liffsendmessages()
liff.sendMessages([{
type: 'text',
text: "Send text message"
}, {
type: 'sticker',
packageId: '2',
stickerId: '144'
}]).then(function () {
window.alert("Sent");
}).catch(function (error) {
window.alert("Error sending message: " + error);
});
});
};
// Get profile and display
function getProfile(){
// https://developers.line.me/en/reference/liff/#liffgetprofile()
liff.getProfile().then(function (profile) {
document.getElementById('useridprofilefield').textContent = profile.userId;
document.getElementById('displaynamefield').textContent = profile.displayName;
var profilePictureDiv = document.getElementById('profilepicturediv');
if (profilePictureDiv.firstElementChild) {
profilePictureDiv.removeChild(profilePictureDiv.firstElementChild);
}
var img = document.createElement('img');
img.src = profile.pictureUrl;
img.alt = "Profile Picture";
img.width = 200;
profilePictureDiv.appendChild(img);
document.getElementById('statusmessagefield').textContent = profile.statusMessage;
}).catch(function (error) {
window.alert("Error getting profile: " + error);
});
}
function initializeApp(data) {
document.getElementById('languagefield').textContent = data.language;
document.getElementById('viewtypefield').textContent = data.context.viewType;
document.getElementById('useridfield').textContent = data.context.userId;
document.getElementById('utouidfield').textContent = data.context.utouId;
document.getElementById('roomidfield').textContent = data.context.roomId;
document.getElementById('groupidfield').textContent = data.context.groupId;
}
3. Push again.
git commit -am "add openwindow"
git push origin master
4. Use same LIFF URL to confirm the behavior.
Close LIFF application
User can close the LIFF by clicking [x] button or tap outside of LIFF area, but you can also close it by using SDK.
1. Replace code of index.html.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>My First LIFF</title>
</head>
<body>
<button id="closewindowbutton">Close LIFF application</button>
<button id="openwindowbutton">Open the window in LINE</button>
<button id="openwindowexternalbutton">Open the window outside of LINE</button>
<button id="sendmessagebutton">Send Message</button>
<div id="profileinfo">
<h2>Profile</h2>
<div id="profilepicturediv">
</div>
<table border="1">
<tr>
<th>userId</th>
<td id="useridprofilefield"></td>
</tr>
<tr>
<th>displayName</th>
<td id="displaynamefield"></td>
</tr>
<tr>
<th>statusMessage</th>
<td id="statusmessagefield"></td>
</tr>
</table>
</div>
<div id="liffdata">
<h2>LIFF Data</h2>
<table border="1">
<tr>
<th>language</th>
<td id="languagefield"></td>
</tr>
<tr>
<th>context.viewType</th>
<td id="viewtypefield"></td>
</tr>
<tr>
<th>context.userId</th>
<td id="useridfield"></td>
</tr>
<tr>
<th>context.utouId</th>
<td id="utouidfield"></td>
</tr>
<tr>
<th>context.roomId</th>
<td id="roomidfield"></td>
</tr>
<tr>
<th>context.groupId</th>
<td id="groupidfield"></td>
</tr>
</table>
</div>
<!-- Load LIFF SDK -->
<script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
<script src="liff.js"></script>
</body>
</html>
2. Replace code of liff.js.
window.onload = function (e) {
// initialize and get basic information
// https://developers.line.me/en/reference/liff/#initialize-liff-app
liff.init(function (data) {
getProfile();
initializeApp(data);
});
// Close LIFF application
// https://developers.line.me/en/reference/liff/#liffclosewindow()
document.getElementById('closewindowbutton').addEventListener('click', function () {
liff.closeWindow();
});
// Open window
// https://developers.line.me/en/reference/liff/#liffopenwindow()
document.getElementById('openwindowbutton').addEventListener('click', function () {
liff.openWindow({
url: 'https://line.me'
});
});
document.getElementById('openwindowexternalbutton').addEventListener('click', function () {
liff.openWindow({
url: 'https://line.me',
external: true
});
});
// Send message
document.getElementById('sendmessagebutton').addEventListener('click', function () {
// https://developers.line.me/en/reference/liff/#liffsendmessages()
liff.sendMessages([{
type: 'text',
text: "Send text message"
}, {
type: 'sticker',
packageId: '2',
stickerId: '144'
}]).then(function () {
window.alert("Sent");
}).catch(function (error) {
window.alert("Error sending message: " + error);
});
});
};
// Get profile and display
function getProfile(){
// https://developers.line.me/en/reference/liff/#liffgetprofile()
liff.getProfile().then(function (profile) {
document.getElementById('useridprofilefield').textContent = profile.userId;
document.getElementById('displaynamefield').textContent = profile.displayName;
var profilePictureDiv = document.getElementById('profilepicturediv');
if (profilePictureDiv.firstElementChild) {
profilePictureDiv.removeChild(profilePictureDiv.firstElementChild);
}
var img = document.createElement('img');
img.src = profile.pictureUrl;
img.alt = "Profile Picture";
img.width = 200;
profilePictureDiv.appendChild(img);
document.getElementById('statusmessagefield').textContent = profile.statusMessage;
}).catch(function (error) {
window.alert("Error getting profile: " + error);
});
}
function initializeApp(data) {
document.getElementById('languagefield').textContent = data.language;
document.getElementById('viewtypefield').textContent = data.context.viewType;
document.getElementById('useridfield').textContent = data.context.userId;
document.getElementById('utouidfield').textContent = data.context.utouId;
document.getElementById('roomidfield').textContent = data.context.roomId;
document.getElementById('groupidfield').textContent = data.context.groupId;
}
3. Push.
git commit -am "add closewindow"
git push origin master
4. Confirm the behavior.
Update or Delete LIFF application
You can update registered LIFF application by using LIFF CLI or you can delete them.
Update
liff update <liffId> <url> <type:full|tall|compact>
Delete
liff delete <liffId>
liff deleteAll
Enjoy LIFF!
Top comments (8)
Hi Kenichiro thanks for the tutorial..
すみません、ちょっと闻いてもいいですか
How to Enable file upload outside of the Album folder in LiFF ?
HTML file upload like below, the opened file browser disable all files and can't be pickup
Album is the only one can be pickup and upload.
Is any way to uplaod file other than images in LIFF?
It's ok when open LIFF in external browser. Is it the limitation of LINE itself?
That's an interesting question, which I don't have answer. If you run the LIFF app in external browser (as v2, you can run outside LINE) what happens?
Thank you for your reply:)
Every thing is fine when running LIFF app in external browser.
In that case I believe it's LINE client limitation for now, unfortunately.
Unable to understand the purpose of this restriction :(
When user install 3rd party file explorer such like "File Manager+", they can pickup any file form it on LIFF app -> upload -> switch to File Manager+
This restriction will only increase the user's inconvenience.
Yeah totally agree. Hope they find a way to solve it. iOS and Android may behave differently?
Hi Kenichiro.. Thanks for the tutorial..
I was not able to download a pdf file inside liff browser but the same is working fine in liff browser. I am using jspdf to generate and download this pdf. Is there any way to download inside liff browser.
Thanks
The first thing you can do it to call the function only to see if Sdk has issue or you didn’t reach to the function.