Maxim Salnikov
@webmaxru
Native Web Apps:
Are We There Yet?
What makes an app feels
like native
and what about web apps
Maxim Salnikov
-
PWA Summit co-organizer
-
PWA Oslo / PWA London meetups organizer
-
Google Dev Expert in Web Tech / Capabilities & Installability
Developer Audience Lead at Microsoft
Web as an app platform
-
Historically depends on the "connection status"
-
Works within the browser context, not underlying platform
-
Evergreen browsers
-
Versatile language
-
Performant JS engines
-
Excellent tooling
-
Huge community
🎂
Web Almanac 2022
Installable websites
~1,500,000 websites may be installable on mobile home screens, offering an app experience.
Reach
"Nativeness"
Native
Web
Web applications can reach anyone, anywhere, on any device with a single codebase.
Nativeness = Number & depth of integrations with the platform
Native VS Web
What's available?
-
Installation & run
-
Windowing
-
Outbound integrations
-
Inbound integrations
-
Notifications
-
Background tasks
-
Access to hardware
Demo app
edge://flags
Installation & run
-
Installation
-
Uninstallation
-
Run on user login
-
Shortcuts
-
Launch mode
Installation & run
-
Installation
-
Uninstallation
-
Run on user login
-
Shortcuts
-
Launch mode
Web App Manifest
Service Worker with "fetch"
{
{
"name": "BPM Techno",
"short_name": "BPM Techno Counter",
"start_url": "?utm_source=homescreen",
"display": "standalone",
"background_color": "#fff",
"description": "A free online BPM counter",
"icons": [{
"src": "images/touch/48x48.png",
"sizes": "48x48",
"type": "image/png"
}]
}
site.webmanifest
Installation & run
-
Installation
-
Uninstallation
-
Run on user login
-
Shortcuts
-
Launch mode
"shortcuts": [
{
"name": "Upload MP3 File",
"short_name": "Upload MP3r",
"description": "Count BPM of the uploaded file",
"url": "/upload-mp3?utm_source=homescreen",
"icons": [{ "src": "/icon-mp3.png", "sizes": "192x192" }]
}
]
site.webmanifest
Installation & run
-
Installation
-
Uninstallation
-
Run on user login
-
Shortcuts
-
Launch mode
"launch_handler": {
"route_to": "new-client" | "existing-client" |
"auto",
"navigate_existing_client": "always" | "never",
}
site.webmanifest
launchQueue.setConsumer(launchParams => {
const url = launchParams.targetURL;
});
main.js
Windowing
-
Main window mode
-
Title bar options
-
Status bar options
-
Tabbed experience
Windowing
-
Main window mode
-
Title bar options
-
Status bar options
-
Tabbed experience
{
"display": "fullscreen" | "standalone" |
"minimal-ui" | "browser",
"display_override": ["window-control-overlay",
"minimal-ui"],
}
site.webmanifest
Windowing
-
Main window mode
-
Title bar options
-
Status bar options
-
Tabbed experience
titlebar-area-x
titlebar-area-y
titlebar-area-width
titlebar-area-height
CSS Variables
navigator.windowControlsOverlay.
getBoundingClientRect()
navigator.windowControlsOverlay.visible
JavaScript API
Windowing
-
Main window mode
-
Title bar options
-
Status bar options
-
Tabbed experience
{
"display_override": "tabbed"
}
site.webmanifest
Outbound integrations
-
Web Share API
-
Contact Picker API
-
File Access API
Outbound integrations
-
Web Share API
-
Contact Picker API
-
File Access API
if (navigator.share) {
navigator.share({
title: 'BPM Techno',
text: 'Check out BPMTech.no',
url: 'https://bpmtech.no',
})
.then(() => console.log('Successful share'))
.catch((err) => console.error(err));
}
main.js
Outbound integrations
-
Web Share API
-
Contact Picker API
-
File Access API
const props = ['name', 'email', 'tel'];
const opts = {multiple: true};
try {
const contacts =
await navigator.contacts.select(props, opts);
handleResults(contacts);
} catch (err) {
console.error(err));
}
main.js
Outbound integrations
-
Web Share API
-
Contact Picker API
-
File Access API
let openFileHandle;
btnOpen.addEventListener('click', async () => {
[openFileHandle] =
await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const contents = await file.text();
});
main.js
const saveFileHandle =
await window.showSaveFilePicker();
const directoryHandle =
await window.showDirectoryPicker();
Inbound integrations
-
URL handlers
-
Declarative Link Capturing
-
Protocol handlers
-
File type handlers
-
Share target
Inbound integrations
-
URL handlers
-
Declarative Link Capturing
-
Protocol handlers
-
File type handlers
-
Share target
"url_handlers": [
{
"origin": "https://bpmtech.no"
},
{
"origin": "https://partnerapp.com"
}
]
site.webmanifest
"web_apps": [
{
"manifest": "https://partnerapp.com/manifest.json",
"details": {
"paths": [
"/public/data/*"
]
}
}
]
.well-known/web-app-origin-association
Inbound integrations
-
URL handlers
-
Declarative Link Capturing
-
Protocol handlers
-
File type handlers
-
Share target
"capture_links":
"none" | "new-client" |
"existing-client-navigate"
site.webmanifest
Inbound integrations
-
URL handlers
-
Declarative Link Capturing
-
Protocol handlers
-
File type handlers
-
Share target
"protocol_handlers": [
{
"protocol": "web+bpm",
"url": "index.html?bpm=%s"
}
]
site.webmanifest
web+bpm://bpm=120
Inbound integrations
-
URL handlers
-
Declarative Link Capturing
-
Protocol handlers
-
File type handlers
-
Share target
"file_handlers": [
{
"action": "/open-mp3",
"accept": { "text/csv": [".mp3"] },
"icons": [
{
"src": "./images/mp3-file.png",
"sizes": "144x144"
}
]
}
]
site.webmanifest
window.launchQueue.setConsumer(launchParams => {
const fileHandle = launchParams.files[0];
});
main.js
Inbound integrations
-
URL handlers
-
Declarative Link Capturing
-
Protocol handlers
-
File type handlers
-
Share target
"share_target": {
"action": "/share",
"method": "GET",
"params": {
"title": "title",
"text": "text",
"url": "url"
}
}
site.webmanifest
window.addEventListener('DOMContentLoaded', ()=> {
const parsedUrl = new URL(window.location);
const url = parsedUrl.searchParams.get('url');
});
main.js
Notifications
-
Web Push API
-
Badging
Notifications
-
Web Push API
-
Badging
navigator.setAppBadge(42).catch((err) => {
console.error(err)
});
navigator.clearAppBadge().catch((err) => {
console.error(err)
});
main.js
Background tasks
-
Background Sync
-
Periodic BG Sync
-
Background Fetch
-
Payment Handler
Access to hardware
- Audio & Video Capture
- Geolocation
- Web Bluetooth
- Web MIDI API
- Magnetometer API
- Web NFC API
- Device Memory API
- Network Information API
- Battery Status API
- Ambient Light Sensor
- Proximity Sensor
- WebHID
- Serial API
- Web USB
- User Idle Detection
Where to follow?
🐡
How to experiment at scale?
Native Web: Are We There?
-
Installation & run
-
Windowing
-
Outbound integrations
-
Inbound integrations
-
Notifications
-
Background tasks
-
Access to hardware
-
Source code
-
Extra features
-
Demo hosted on Azure Static Web Apps
Extended PWA example
Thank you!
@webmaxru
Maxim Salnikov
Questions?
@webmaxru
Maxim Salnikov
Where to host your native web app?
Native Web Apps: Are We There Yet?
By Maxim Salnikov
Native Web Apps: Are We There Yet?
There are so many discussions about web VS native apps. Will we get to the point where the Web becomes truly Native for the majority of the platforms? Progressive Web Apps, Project Fugu, WebAssembly & other technologies actively contribute to moving in this direction. In this session, we go through a list of details that make the integration of an application & operating system seamless, and map it to the APIs available for the web platform. With the knowledge of what's available today and what's coming soon, you are empowered to build truly Native Web Apps to deliver the best user experience!
- 3,405