<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>firt.dev</title>
	<subtitle>News, articles, ideas, and notes on mobile+web development.</subtitle>
	
	<link href="https://firt.dev/feed/feed.xml" rel="self"/>
	<link href="https://firt.dev/"/>
	<updated>2026-10-13T00:00:00Z</updated>
	<id>https://firt.dev/</id>
	<author>
		<name>Maximiliano Firtman</name>
		<email>hi@firt.dev</email>
	</author>
	
	<entry>
		<title>iOS PWA Compatibility </title>
		<link href="https://firt.dev/notes/pwa-ios/"/>
		<updated>2026-10-13T00:00:00Z</updated>
		<id>https://firt.dev/notes/pwa-ios/</id>
		<description>
		
			What&#39;s supported on Safari on iOS and iPadOS for Progressive Web Apps
				
		</description>
		<content type="html">&lt;p&gt;This is a compatibility list of supported features on Safari on iOS and iPadOS vs. what&#39;s available in the most used PWA browser in current market share for similar mobile devices: Google Chrome on Android.&lt;/p&gt;
&lt;blockquote class=&quot;warning&quot;&gt;
&lt;p&gt;This list is based and maintained on private tests I frequently do. WebKit is getting better documenting these things, but there is still a huge gap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;high-level-pwa-support&quot;&gt;High-level PWA support &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#high-level-pwa-support&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ability&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;th&gt;Since version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Offline Support with Service Workers&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Installation from Browser&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3 (from 2.0 with &lt;code&gt;meta&lt;/code&gt; tags). Since 26.0 every website can be installed as a web app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Installation from Store&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;14.0 with WebView and App-Bound domains (no official announcement)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Installation for Enterprise&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;10.0 (MobileConfig file)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Installation Prompt or Banner&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singleton Installation&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Installation Recovery with Backup&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Link Capturing&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Only a push message can open an installed PWA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In-App Browser for out-of-scope links&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;12.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage shared with Browser&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App appears in Search&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3 (from 4.0 with &lt;code&gt;meta&lt;/code&gt; tags)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App appears in Multitasking list&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3 (from 2.0 with &lt;code&gt;meta&lt;/code&gt; tags)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App appears in Homescreen&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3 (from 2.0 with &lt;code&gt;meta&lt;/code&gt; tags)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App appears in App Gallery Folders&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App can share screen with other apps&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;14.0 (iPadOS-only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App can be installed from non-Safari browsers&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;16.4, if opted-in by the browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App can be installed from Safari View Controller&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;17.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Login cookies copied to the installed app&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;17.2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;Since iOS and iPadOS 26, every website your users add to the Home Screen opens as a web app by default, even if it doesn&#39;t have a manifest or the &lt;code&gt;apple-mobile-web-app-capable&lt;/code&gt; meta tag. Users can turn off &lt;em&gt;Open as Web App&lt;/em&gt; when adding it to get a browser bookmark instead. If your app has a manifest, Safari still uses it, including its icons.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;web-app-manifest-support&quot;&gt;Web App Manifest Support &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#web-app-manifest-support&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;w3c-spec-fields&quot;&gt;W3C Spec Fields &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#w3c-spec-fields&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;th&gt;Since version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dir&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lang&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;short_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scope&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;icons&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;display&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;orientation&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;start_url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;16.4 (it&#39;s concatenated with icon&#39;s name)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;theme_color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;related_applications&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prefer_related_applications&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;background_color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;shortcuts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Only on macOS since 17.4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;Before iOS 15.4, icons for PWAs can only be set by the non-standard &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; element with &lt;code&gt;rel=apple-touch-icon&lt;/code&gt;. If you still have that element, it overrides your manifest &lt;code&gt;icons&lt;/code&gt; declaration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;display-options&quot;&gt;Display options &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#display-options&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;th&gt;Fallbacks To&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;browser&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;standalone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minimal-ui&lt;/code&gt;¹&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;browser&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fullscreen&lt;/code&gt;¹&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;standalone&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;icon-options&quot;&gt;Icon Options &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#icon-options&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PNG Image&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SVG Image⁴&lt;/td&gt;
&lt;td&gt;✅ since 26.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maskable Icon¹&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monochrome Icon¹&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;¹ &lt;small&gt;WebKit recognizes these options, but it ignores them. The &lt;code&gt;apple-touch-icon&lt;/code&gt; link element in the HTML overrides the manifest icon declaration.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;⁴ &lt;small&gt;SVG icons only work when you declare them in the manifest. If you use an SVG in the &lt;code&gt;apple-touch-icon&lt;/code&gt; link element or as a favicon, Safari downloads it but doesn&#39;t use it for the Home Screen icon.&lt;/small&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The list of fields comes from the latest &lt;a href=&quot;https://www.w3.org/TR/appmanifest/&quot;&gt;W3C Working Draft&lt;/a&gt;. What&#39;s supported on WebKit can be seen by testing or by checking the &lt;a href=&quot;https://github.com/WebKit/WebKit/blob/8b3d621b2da4620757903dd4c4d67f36951ba7f6/Source/WebCore/Modules/applicationmanifest/ApplicationManifest.h&quot;&gt;WebKit Source code (ApplicationManifest.h)&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;apple-non-standard-pwa-related-abilities&quot;&gt;Apple non-standard PWA-related abilities &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#apple-non-standard-pwa-related-abilities&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Supported since&lt;/th&gt;
&lt;th&gt;Should you use it?&lt;/th&gt;
&lt;th&gt;Replaced by&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apple-mobile-web-app-capable&lt;/code&gt; meta&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;🫱 since 11.3 it&#39;s optional²; since 26.0 it&#39;s not needed to open as an app&lt;/td&gt;
&lt;td&gt;&lt;code&gt;display: standalone&lt;/code&gt; in manifest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apple-mobile-web-app-title&lt;/code&gt; meta&lt;/td&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;td&gt;👎 since 11.3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt; in manifest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apple-mobile-web-app-status-bar-style&lt;/code&gt; meta&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;👎 since 15.0&lt;/td&gt;
&lt;td&gt;&lt;code&gt;theme-color&lt;/code&gt; meta tag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;black-translucent&lt;/code&gt; status bar meta&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;👍 Still the only way to get a fullscreen app&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apple-touch-icon&lt;/code&gt; link&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;🫱 (optional since 15.4 but still useful)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;icons&lt;/code&gt; in manifest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apple-touch-startup-image&lt;/code&gt; link&lt;/td&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;td&gt;👍 Still the only way to define splash screens²&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;navigator.standalone&lt;/code&gt; flag&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;td&gt;👎 since 11.3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;display-mode&lt;/code&gt; media query&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;² &lt;small&gt;Startup images only work if the &lt;code&gt;apple-mobile-web-app-capable&lt;/code&gt; meta tag is present despite the presence of the manifest standalone replacement.&lt;/small&gt;&lt;/p&gt;
&lt;h2 id=&quot;manifest-incubations&quot;&gt;Manifest Incubations &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#manifest-incubations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;th&gt;Since version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;display_override&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;share_target&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;note_taking&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;protocol_handlers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;appinstalled&lt;/code&gt; event&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;beforeinstallprompt&lt;/code&gt; event (installation prompt)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;The list of fields comes from the latest &lt;a href=&quot;https://wicg.github.io/manifest-incubations/&quot;&gt;Web Application Manifest Incubators&lt;/a&gt; supported in Google Chrome for Android (stable or experimental).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;other-pwa-related-desktop-abilities&quot;&gt;Other PWA-related desktop abilities &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#other-pwa-related-desktop-abilities&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These abilities may only be suitable for iPadOS devices.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ability&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;th&gt;Since version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Window Controls Overlay&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tabbed Display&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id=&quot;service-workers-support&quot;&gt;Service Workers Support &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#service-workers-support&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ability&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;th&gt;Since version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fetch API&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;10.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Service Worker Support&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache Storage interface&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UpdateViaCache at Registration&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streams&lt;/td&gt;
&lt;td&gt;✅ (partial)&lt;/td&gt;
&lt;td&gt;11.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation Preload&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static Routing&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;27.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;abilities-on-top-of-service-workers&quot;&gt;Abilities on Top of Service Workers &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#abilities-on-top-of-service-workers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ability&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;th&gt;Since version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Web Push&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;16.4 only for installed PWAs³&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Declarative Web Push&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;18.4 only for installed PWAs; it doesn&#39;t need a service worker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Background Sync&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Periodic Background Sync&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Background Fetch&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;³ &lt;small&gt;Web Push only works if the web app is installed in the home screen; not available in Safari or other browsers&lt;/small&gt;&lt;/p&gt;
&lt;h1 id=&quot;high-level-overview-of-web-abilities-for-pwas&quot;&gt;High-level overview of Web abilities for PWAs &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#high-level-overview-of-web-abilities-for-pwas&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ability&lt;/th&gt;
&lt;th&gt;Supported&lt;/th&gt;
&lt;th&gt;Since version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Geolocation&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Storage&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IndexedDB&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Workers&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;5.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared Web Workers&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;5.0 to 6.1, back in 16.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2D Canvas&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3D Canvas: WebGL&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;8.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3D Canvas: WebGL 2.0&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebGPU&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;26.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web XR (VR &amp;amp; AR)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Only for visionOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Assembly&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clipboard&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;12.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credential Management&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;14.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment Request&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;12.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment Handler&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Motion Sensors&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;4.2 to 12.1, back in 13.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Touch Events&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pointer Events&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;13.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Camera and Microphone Access (getUserMedia)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;13.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Advanced Camera Controls&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Media Recorder&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;14.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Media Session&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Speech Synthesis&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;7.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Speech Recognition&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;14.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Bluetooth&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebAudio&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;6.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSerial&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebNFC&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebHID&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Battery Status&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ambient Light&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vibration&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GamePad&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;10.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screen Wake Lock&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;16.4 (18.4 for installed PWAs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Activation&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;16.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device Memory&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idle Detection&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screen Orientation&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;16.4 (partial support)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Presentation&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pointer Lock&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network Information&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screen Capture&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shape Detection&lt;/td&gt;
&lt;td&gt;❌⏳&lt;/td&gt;
&lt;td&gt;17 (experimental)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMS OTP &lt;code&gt;input&lt;/code&gt; attribute&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;14.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMS WebOTP API&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Virtual Keyboard&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File through &lt;code&gt;input&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;6.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FileSystem Access (public fs)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FileSystem Access (origin private fs)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.2 (writable streams since 26.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage Management: Persistent&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage Management: Quota&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contact Picker&lt;/td&gt;
&lt;td&gt;⏳&lt;/td&gt;
&lt;td&gt;14.5 (experimental)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cookie Store&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;18.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Sockets&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;4.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebTransport&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;26.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebRTC&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;11.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Codecs&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;16.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Notifications&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;16.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Badging&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;16.4 only for installed PWAs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fullscreen&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;8.0 (iPad only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page Visibility&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;7.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Authentication&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;14.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Digital Credentials&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;26.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Share&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;12.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Web Share 2.0&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;15.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get Installed Related Apps&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;safari-only-abilities-for-pwas&quot;&gt;Safari-only abilities for PWAs &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/notes/pwa-ios/#safari-only-abilities-for-pwas&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ability&lt;/th&gt;
&lt;th&gt;Since version&lt;/th&gt;
&lt;th&gt;Standard replacement?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Apple Pay JS&lt;/td&gt;
&lt;td&gt;10.0&lt;/td&gt;
&lt;td&gt;Payment Request (compatible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick Look for Augmented Reality&lt;/td&gt;
&lt;td&gt;12.0&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;model&amp;gt;&lt;/code&gt; Element for Augmented Reality&lt;/td&gt;
&lt;td&gt;17.0&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebMetal&lt;/td&gt;
&lt;td&gt;12.2 (experimental)&lt;/td&gt;
&lt;td&gt;WebGPU (since 26.0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safari Push Notifications (mac-only)&lt;/td&gt;
&lt;td&gt;not on iOS&lt;/td&gt;
&lt;td&gt;Web Push (Safari 16)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gesture touch events&lt;/td&gt;
&lt;td&gt;4.0&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Smart App Banners&lt;/td&gt;
&lt;td&gt;6.0&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sign In with Apple&lt;/td&gt;
&lt;td&gt;13.0&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a quick and dirty list of abilities in the Web Platform and its compatibility in Safari and installed PWAs. Each ability has many properties or sub-abilities that may or may not be available on every browser, so check &lt;a href=&quot;https://caniuse.com/&quot;&gt;Can I Use&lt;/a&gt; to see how it works on iOS and iPadOS. Use always &lt;em&gt;Feature Detection&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Contact me if you want to suggest a feature or report a bug in these tables.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Unlocking Client-Side AI in Your Web App </title>
		<link href="https://firt.dev/client-side-ai/"/>
		<updated>2026-09-18T00:00:00Z</updated>
		<id>https://firt.dev/client-side-ai/</id>
		<description>
		
			
				
		</description>
		<content type="html">&lt;p&gt;During dotJS 2026 in Paris 🇫🇷 I delivered this talk about running generative AI entirely on the client side&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=kJ5GyP9HGio&quot;&gt;https://www.youtube.com/watch?v=kJ5GyP9HGio&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You don&#39;t need a cloud subscription or backend infrastructure to bring powerful AI features into your web app. Thanks to modern browser APIs and local model support, it&#39;s now possible to run generative AI — including text, vision, and speech — entirely on the client side.&lt;/p&gt;
&lt;p&gt;In this session, you&#39;ll learn how to unlock AI capabilities in the browser using built-in AI APIs, WebNN, WebGPU, and WebAssembly. We&#39;ll explore how on-device models, such as Gemini Nano in Chrome, and open-source models enable local inference for language, vision, and audio: privately, offline, and without recurring cloud costs.&lt;/p&gt;
&lt;p&gt;We&#39;ll cover architecture patterns, browser and device capabilities, and live demos, including how to detect support and decide when to use built-in models, your own client-side models, or a cloud fallback.&lt;/p&gt;
&lt;p&gt;If you&#39;re building for the Web, this talk shows how to add real AI to your web app: locally, privately, and using technologies available in the browser today.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>WebMCP: Making Web Apps Faster and Cheaper for Coding Agents </title>
		<link href="https://firt.dev/webmcp/"/>
		<updated>2026-06-18T00:00:00Z</updated>
		<id>https://firt.dev/webmcp/</id>
		<description>
		
			
				
		</description>
		<content type="html">&lt;p&gt;During AI DevCon 2026 in London 🇬🇧 I delivered this talk about WebMCP and how web apps can expose tools to AI agents&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=BV7RYioryKE&quot;&gt;https://www.youtube.com/watch?v=BV7RYioryKE&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Coding agents still use web apps the hard way: taking screenshots, scraping the DOM and guessing where to click. That wastes tokens, it&#39;s slow, and it breaks as soon as the UI changes.&lt;/p&gt;
&lt;p&gt;In this session, you&#39;ll see how WebMCP changes that by letting your web app expose its own tools to the agent with a clear contract. We&#39;ll cover where WebMCP fits in the MCP ecosystem and next to browser automation, what you gain by exposing tools instead of letting agents drive the UI, and its current experimental status at the W3C.&lt;/p&gt;
&lt;p&gt;We&#39;ll also go through the security considerations and the current limitations, so you know what you can ship today and what is still coming.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Vanilla Web Book 🆕 </title>
		<link href="https://firt.dev/notes/vanilla-web-meap/"/>
		<updated>2026-05-05T00:00:00Z</updated>
		<id>https://firt.dev/notes/vanilla-web-meap/</id>
		<description>
		
			Building apps without frameworks, early access!
				
		</description>
		<content type="html">&lt;figure&gt;&lt;img src=&quot;https://firt.imgix.net/img/notes/vanilla-web-2.jpg?auto=format&quot; alt=&quot;&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;I&#39;m happy to annouce my 15th book: &lt;a href=&quot;https://hubs.la/Q03_tWrr0&quot;&gt;Vanilla Web&lt;/a&gt; published by Manning Books. It&#39;s currently available at MEAP (Early Access Program): you can read it while it&#39;s being written and get the final eBook or printed copy as soon as it&#39;s finished.&lt;/p&gt;
&lt;p&gt;When it comes to web development, vanilla may be the finest of the flavors. With plain JavaScript you can build fast, maintainable web apps that run everywhere, load instantly, and avoid framework lock-in. Today’s browsers are incredibly capable, and for many types of applications, “batteries included” libraries and frameworks just introduce avoidable slowdowns, security issues, and complex build chains. Why not embrace the vanilla web!&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://firt.imgix.net/img/notes/vanilla-web-1.png?auto=format&quot; alt=&quot;&quot; /&gt;&lt;/figure&gt;
&lt;p&gt;Vanilla Web is your guide to building serious web applications using the tools built right into the browser. You’ll learn to use “plain vanilla” JavaScript, HTML, and CSS to create reusable UI components, a routing system, an installable progressive web app, and a full-featured e-commerce frontend. Each chapter builds real capabilities you can apply anywhere, even when you work with libraries. You’ll soon be delivering dependency-free UX that’s easy to debug and maintain and survive neverending tool churn.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hubs.la/Q03_tWrr0&quot; class=&quot;button&quot; target=&quot;_blank&quot;&gt;Get access to the book now, MEAP edition&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Chapters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hello Vanilla Web&lt;/li&gt;
&lt;li&gt;Understanding the Web Platform&lt;/li&gt;
&lt;li&gt;The User Interface&lt;/li&gt;
&lt;li&gt;Vanilla CSS&lt;/li&gt;
&lt;li&gt;Vanilla JavaScript&lt;/li&gt;
&lt;li&gt;The Document Object Model API&lt;/li&gt;
&lt;li&gt;Web Components&lt;/li&gt;
&lt;li&gt;Working with HTML Templates&lt;/li&gt;
&lt;li&gt;Advanced Web Components&lt;/li&gt;
&lt;li&gt;Navigation and Routing &lt;em&gt;(coming later)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Working with Data &lt;em&gt;(coming later)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Micro-libraries &lt;em&gt;(coming later)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Beyond Basics &lt;em&gt;(coming later)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Bulding and App &lt;em&gt;(coming later)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://hubs.la/Q03_tWrr0&quot; class=&quot;button&quot; target=&quot;_blank&quot;&gt;Get access to the book now, MEAP edition&lt;/a&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Enhancing User Engagement with JavaScript </title>
		<link href="https://firt.dev/user-engagement-js/"/>
		<updated>2025-12-06T00:00:00Z</updated>
		<id>https://firt.dev/user-engagement-js/</id>
		<description>
		
			1.5-hours online video training for web developers
				
		</description>
		<content type="html">&lt;figure&gt;&lt;img src=&quot;https://firt.imgix.net/img/courses/ps-user-engagement-js.png?auto=format&quot; alt=&quot;&quot; /&gt;&lt;figcaption&gt;Available for Pluralsight subscribers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Creating a good Progressive Web App is not just about the user interface, but also about understanding the platform, what&#39;s possible, and how to make a proper integration with the operating system.&lt;/p&gt;
&lt;p&gt;Web technologies have grown beyond static pages and simple interactions. Today’s users expect web experiences that respond to their context and behavior.&lt;/p&gt;
&lt;p&gt;In this course, Enhancing User Engagement with JavaScript, you’ll gain the ability to build dynamic, interactive experiences by leveraging modern browser APIs.&lt;/p&gt;
&lt;p&gt;First, you’ll explore the types of APIs available in browsers and how permission management works. Next, you’ll discover how to use Geolocation, Camera, and Motion APIs to tap into user devices. Finally, you’ll learn how to store and retrieve data locally using different browser APIs.&lt;/p&gt;
&lt;p&gt;When you’re finished with this course, you’ll have the skills and knowledge in JavaScript and HTML needed to create engaging, user-aware web applications.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.pluralsight.com/courses/enhancing-user-engagement-javascript&quot; class=&quot;button&quot; target=&quot;_blank&quot;&gt;Take the course&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this 1.5 hours course we&#39;ll see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Capabilities on the Web&lt;/li&gt;
&lt;li&gt;Feature Detection&lt;/li&gt;
&lt;li&gt;Working with Permissions&lt;/li&gt;
&lt;li&gt;Geolocation Essentials&lt;/li&gt;
&lt;li&gt;The Geolocation API&lt;/li&gt;
&lt;li&gt;Using Geocoding services&lt;/li&gt;
&lt;li&gt;Using Mapping services&lt;/li&gt;
&lt;li&gt;Working with Sensors&lt;/li&gt;
&lt;li&gt;Screen Orientation API&lt;/li&gt;
&lt;li&gt;DeviceMotion and DeviceOrientation&lt;/li&gt;
&lt;li&gt;The Sensors API&lt;/li&gt;
&lt;li&gt;Client-Side Data Storage&lt;/li&gt;
&lt;li&gt;Quotas&lt;/li&gt;
&lt;li&gt;Data Persistance&lt;/li&gt;
&lt;li&gt;Session and Local Storage&lt;/li&gt;
&lt;li&gt;IndexedDB&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also request a live custom workshop for you or your team on the topics of this course. &lt;a href=&quot;https://firt.dev/about&quot;&gt;Let&#39;s keep in touch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
	</entry>
	
	<entry>
		<title>The Immersive Web </title>
		<link href="https://firt.dev/web-inmersiva/"/>
		<updated>2025-11-15T00:00:00Z</updated>
		<id>https://firt.dev/web-inmersiva/</id>
		<description>
		
			JSConf Mexico session (in spanish)
				
		</description>
		<content type="html">&lt;p&gt;During JSConf Mexico in Guadalajara 🇲🇽, I&#39;ve delivered this session covering the latest ideas on WebXR, the &lt;code&gt;&amp;lt;model&amp;gt;&lt;/code&gt; HTML Element and other ideas. Session delivered in Spanish.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=HMM41y9VjY4&quot;&gt;https://www.youtube.com/watch?v=HMM41y9VjY4&lt;/a&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>JavaScript Promises from Scratch </title>
		<link href="https://firt.dev/promises-from-scratch/"/>
		<updated>2025-09-11T00:00:00Z</updated>
		<id>https://firt.dev/promises-from-scratch/</id>
		<description>
		
			Vanilla JavaScript Tutorial
				
		</description>
		<content type="html">&lt;figure&gt;&lt;img src=&quot;https://firt.imgix.net/img/courses/fm-tutorial-promises.jpg?auto=format&quot; alt=&quot;&quot; /&gt;&lt;figcaption&gt;Available for Frontend Masters subscribers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Learn everything about the Promises design pattern and how to use it in JavaScript. Create Promises with constructors and async functions, and consume them using callbacks or async/await along with proper error handling. Master sequential and parallel execution with the Promises API.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/tutorials/firt/promises-from-scratch/?code=firtman&amp;utm_source=firtman&amp;utm_medium=social&amp;utm_campaign=teacher_coupon&quot; class=&quot;button&quot; target=&quot;_blank&quot;&gt;See the tutorial &lt;br /&gt;$10 OFF First Month&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also request a live custom workshop for you or your team on the topics of this course. &lt;a href=&quot;https://firt.dev/about&quot;&gt;Let&#39;s keep in touch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
	</entry>
	
	<entry>
		<title>Advanced Vanilla JS with Go </title>
		<link href="https://firt.dev/adv-vanillajs-go/"/>
		<updated>2025-06-02T00:00:00Z</updated>
		<id>https://firt.dev/adv-vanillajs-go/</id>
		<description>
		
			3.5-hour online video workshop for web professionals
				
		</description>
		<content type="html">&lt;figure&gt;&lt;img src=&quot;https://firt.imgix.net/img/courses/fm-adv-vanillago.jpg?auto=format&quot; alt=&quot;&quot; /&gt;&lt;figcaption&gt;Available for Frontend Masters subscribers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Modernize your full-stack app with Passkeys! Watch as Maximiliano Firtman refactors a Vanilla JS + Go movie database application to allow users to sign in using device security capabilities using Passkeys like Touch ID and Face ID -- which are more secure and easier to use than both passwords and all current 2-factor authentication (2FA) methods.&lt;/p&gt;
&lt;p&gt;You&#39;ll also learn how to improve SEO with server-side rendering, add offline support with Service Workers, and even convert the full client-side app into a Progressive Web App (PWA).&lt;/p&gt;
&lt;p&gt;Want to see this application built from scratch? Watch the &lt;a href=&quot;https://firt.dev/vanillajs-go/&quot;&gt;Build a Fullstack App with Vanilla JS and Go course&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=qn8isz7keLc&amp;amp;ab_channel=FrontendMasters&quot;&gt;https://www.youtube.com/watch?v=qn8isz7keLc&amp;amp;ab_channel=FrontendMasters&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=qn8isz7keLc&amp;ab_channel=FrontendMasters&quot; class=&quot;button&quot; target=&quot;_blank&quot;&gt;Watch this Course FREE&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Outline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Project Tour &amp;amp; Setup&lt;/li&gt;
&lt;li&gt;Passkeys Overview&lt;/li&gt;
&lt;li&gt;What is a Passkey&lt;/li&gt;
&lt;li&gt;Passkey Services&lt;/li&gt;
&lt;li&gt;Passkey Use Case&lt;/li&gt;
&lt;li&gt;Implementing Passkeys&lt;/li&gt;
&lt;li&gt;Creating Passkey Handlers&lt;/li&gt;
&lt;li&gt;Registering the Passkey Handler&lt;/li&gt;
&lt;li&gt;Adding Passkeys to the Form&lt;/li&gt;
&lt;li&gt;SimpleWebAuthn Passkey Services&lt;/li&gt;
&lt;li&gt;Logging in with a Passkey&lt;/li&gt;
&lt;li&gt;Conditional UI&lt;/li&gt;
&lt;li&gt;Improving SEO with Server-Side Rendering&lt;/li&gt;
&lt;li&gt;Rendering Details Page on the Server&lt;/li&gt;
&lt;li&gt;Offline Support with Service Workers&lt;/li&gt;
&lt;li&gt;Intercepting Network Requests&lt;/li&gt;
&lt;li&gt;Registering a Service Worker&lt;/li&gt;
&lt;li&gt;Adding Offline Support to the Project&lt;/li&gt;
&lt;li&gt;Background Sync &amp;amp; PWAs&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also request a live custom workshop for you or your team on the topics of this course. &lt;a href=&quot;https://firt.dev/about&quot;&gt;Let&#39;s keep in touch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
	</entry>
	
	<entry>
		<title>Build a Fullstack App with Vanilla JS and Go </title>
		<link href="https://firt.dev/vanillajs-go/"/>
		<updated>2025-05-25T00:00:00Z</updated>
		<id>https://firt.dev/vanillajs-go/</id>
		<description>
		
			10-hours online video workshop for web professionals
				
		</description>
		<content type="html">&lt;figure&gt;&lt;img src=&quot;https://firt.imgix.net/img/courses/fm-vanillago.webp?auto=format&quot; alt=&quot;&quot; /&gt;&lt;figcaption&gt;Available for Frontend Masters subscribers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Code a high-performance fullstack app from scratch! Skip the framework and create a modern Vanilla JavaScript app with web components, dynamic client-side routing, View Transitions, and data filtering and sorting. Build a rock-solid backend JSON API with Go, complete with authentication, logging, and a Postgres database layer. Learn to build robust applications, with higher performance and fewer dependencies, in Vanilla JS and Go!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/courses/vanilla-js-go/?code=firtman&amp;utm_source=firtman&amp;utm_medium=social&amp;utm_campaign=teacher_coupon&quot; class=&quot;button&quot; target=&quot;_blank&quot;&gt;Watch Course &lt;br /&gt;$10 OFF first month&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this 10-hour course we&#39;ll see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Introduction to the Project&lt;/li&gt;
&lt;li&gt;Setting Up the Backend with Go&lt;/li&gt;
&lt;li&gt;Database Schema and REST API&lt;/li&gt;
&lt;li&gt;Lunch&lt;/li&gt;
&lt;li&gt;Setting Up the Frontend&lt;/li&gt;
&lt;li&gt;Web Components&lt;/li&gt;
&lt;li&gt;Dynamic Rendering&lt;/li&gt;
&lt;li&gt;Recap and Pending Issues&lt;/li&gt;
&lt;li&gt;Dynamic Data Retrieval: Search, Filter &amp;amp; Sort&lt;/li&gt;
&lt;li&gt;Client-Side Routing&lt;/li&gt;
&lt;li&gt;Lunch&lt;/li&gt;
&lt;li&gt;State Management&lt;/li&gt;
&lt;li&gt;Implementing Authentication&lt;/li&gt;
&lt;li&gt;Adding Favorites and Watchlists&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Continue the journey of this course in the &lt;a href=&quot;https://firt.dev/adv-vanillajs-go&quot;&gt;Advanced Vanilla JS with Go&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also request a live custom workshop for you or your team on the topics of this course. &lt;a href=&quot;https://firt.dev/about&quot;&gt;Let&#39;s keep in touch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
	</entry>
	
	<entry>
		<title>Understanding JavaScript in the Background </title>
		<link href="https://firt.dev/understanding-js-background/"/>
		<updated>2025-05-19T00:00:00Z</updated>
		<id>https://firt.dev/understanding-js-background/</id>
		<description>
		
			What happens to JS code running in a web app when you switch tasks?
				
		</description>
		<content type="html">&lt;p&gt;Have you ever wondered what happens to the JavaScript code running in a web app when you switch tasks? Consider these scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You&#39;re using a React web app on your phone, and amidst network operations, you reply to a WhatsApp message with a long audio recording.&lt;/li&gt;
&lt;li&gt;You&#39;re playing a game in a PWA on your PC and minimize it to take a Zoom call.&lt;/li&gt;
&lt;li&gt;You&#39;re using ChatGPT on your iPad and open a new tab to check sports scores.&lt;/li&gt;
&lt;li&gt;You&#39;re using X in Chrome on your Android and receive a DM right after switching to the home screen to play a game.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Does the JavaScript code in these scenarios finish executing? What about timers, animations, and pending network requests? Do these web apps continue to consume CPU resources in the background? Can they still receive messages?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It&#39;s time to delve into these questions and understand how JavaScript behaves in the background.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;background-matters&quot;&gt;Background Matters &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#background-matters&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every web app deployed today, no matter if it&#39;s Vanilla JavaScript, or if it&#39;s using a library or framework, such as Angular, React, Svelte or Next.js, needs to understand how code execution works when interacting with modern browsers and operating system. Many times, we as developers, think that our code is executed from beginning to end without interruptions, and we don&#39;t even sit down and think what happens with our JavaScript context when our code moves to the background.&lt;/p&gt;
&lt;p&gt;When we think about background processes in JavaScript, the concept is often shrouded in mystery. Many web developers don’t delve deeply into the nuances of background behavior, typically focusing on active user interactions instead. But understanding what happens in the background—when a user minimizes an app, switches tabs, or closes a browser—can unlock opportunities to enhance performance, optimize resource usage, and improve user experience.&lt;/p&gt;
&lt;h2 id=&quot;defining-%E2%80%9Cbackground%E2%80%9D-in-web-applications&quot;&gt;Defining “Background” in Web Applications &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#defining-%E2%80%9Cbackground%E2%80%9D-in-web-applications&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The term &amp;quot;background&amp;quot; has varying interpretations in web development. It could refer to a tab that is not active while the browser is still active, a browser running in the background of the operating system, or even threads executing tasks outside the main thread. There is no single, clear definition, even in the specifications provided by the W3C.&lt;/p&gt;
&lt;p&gt;Let’s consider a working definition for the scope of this discussion: background refers to any state where our web app has been paused or stopped from executing code or interacting with the user. This could be due to minimizing the app, switching to a different window, a model important dialog from the operating system or putting the browser or the standalone web itself in the background.&lt;/p&gt;
&lt;p&gt;Understanding this concept is crucial, as it dictates how developers optimize resources and maintain seamless user experiences across devices and platforms.&lt;/p&gt;
&lt;h2 id=&quot;execution-engines&quot;&gt;Execution Engines &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#execution-engines&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These days, JavaScript can be executed in many contexts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;eBooks and PDFs&lt;/li&gt;
&lt;li&gt;The frontend&lt;/li&gt;
&lt;li&gt;The backend with Node.js or Deno&lt;/li&gt;
&lt;li&gt;As a middleware in the network layer, like when creating workers for Cloudflare.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this article, we&#39;ll be focusing on frontend JavaScript, which means focusing on a web application, that can be executed in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The browser&lt;/li&gt;
&lt;li&gt;Installed web app (also known as a PWA or Progressive Web App). They have their own icon in the operating system and typically show as a standalone window. We will use the term &amp;quot;standalone web app&amp;quot; to refer to these contexts.&lt;/li&gt;
&lt;li&gt;Other native apps using:
&lt;ul&gt;
&lt;li&gt;Hybrid approaches such as Apache Cordova or Ionic&#39;s Capacitor&lt;/li&gt;
&lt;li&gt;In-app browsers from other apps (such as TikTok or Instagram)&lt;/li&gt;
&lt;li&gt;A mini app framework (such as Telegram Mini Apps, WeChat Mini Programs, World mini apps or Douyin Mini Programs).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;common-background-scenarios&quot;&gt;Common Background Scenarios &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#common-background-scenarios&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To better understand JavaScript behavior in the background, let’s consider typical use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Switching to a New Tab or Browser Window:&lt;/strong&gt; The current tab moves to the background, but the browser is still open. This means that your web app is not visible (you may see only the tab with the favicon and title).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sharing the Space with Other Active Apps:&lt;/strong&gt; Typically, on mobile devices, this means that your web app is still partially visible on the screen but other windows, including modal dialogs from the operating system, may be rendered on top of it having the user focus.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimizing the Browser or App:&lt;/strong&gt; The browser remains open but is no longer visible on the screen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Closing a Tab, a Standalone or a Browser Window:&lt;/strong&gt; The user closes a tab on mobile and desktop or closes a browser on desktop. On mobile devices, users don&#39;t typically have an option to close a browser, they just go to the home screen or switch to another app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Switching Between Apps:&lt;/strong&gt; The browser or standalone web app window is not anymore on the screen. On desktop devices it may be minimized, on mobile devices, switching apps often suspends the previous app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OS-Level Processes:&lt;/strong&gt; The operating system may decide to kill processes to free up memory, which could affect web apps. Users may also decide to kill apps from their operating systems.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these scenarios impacts JavaScript execution in different ways, depending on browser and OS behavior. Understanding that this can happen will help to improve user experience, security and performance of your web app.&lt;/p&gt;
&lt;h2 id=&quot;why-background-execution-matters&quot;&gt;Why Background Execution Matters &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#why-background-execution-matters&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Optimizing JavaScript for background execution is crucial for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Improve User Experience:&lt;/strong&gt; Preventing outdated content or broken states improves usability. For example, resuming a game should not show progress that occurred while the app was in the background. Another example: returning to a web app that was inactive for 24 hours may not resume timers as if the user was active. It should restart, reload the authentication token, or refresh the data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource Efficiency:&lt;/strong&gt; Reducing unnecessary network requests, timers, and animations saves memory and battery life.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoiding Broken Transactions:&lt;/strong&gt; Managing pending requests ensures that data integrity is maintained when users switch contexts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session Management:&lt;/strong&gt; When a web app is paused for hours or even days, it&#39;s crucial to manage the session properly upon resumption. The time difference can impact how the app functions, requiring decisions like resuming normal activity, logging out the user to get a new authorization token, or refreshing the data. Without proper session management, the web app will be unaware of the elapsed time and may not function correctly.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;lifecycle-of-background-execution&quot;&gt;Lifecycle of Background Execution &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#lifecycle-of-background-execution&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Understanding the lifecycle of background execution involves analyzing how web apps transition through various states:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Active State&lt;/strong&gt;: JavaScript runs without restrictions. Animations, timers, and event listeners work as expected.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paused / Not Visible State&lt;/strong&gt;: JavaScript runs with restrictions. Event listeners may not work, animations - **may be paused, and timers slow down.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Suspended / Frozen State&lt;/strong&gt;: The JavaScript context remains in memory, but main execution is paused. For example, animations stop, and timers are not fired. The app resumes to the Active Stte if the user - **returns to the web app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Destroy / Terminated&lt;/strong&gt;: The JavaScript context and the whole web app is unloaded from memory. The app must restart if the user returns to the web app.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;mobile-device-nuances&quot;&gt;Mobile Device Nuances &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#mobile-device-nuances&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Mobile operating systems like Android and iOS introduce additional complexities. Apps are often suspended rather than closed, and users’ perceptions of task managers do not align with the technical realities of process management. In these cases, the OS takes control over resources to enhance battery life and optimize performance, leading to variations in background behavior across platforms.&lt;br /&gt;
We won&#39;t get into deep details on the lifecycle of apps on iOS and Android, but we need to remember that the browser that executes our app is a native app within those operating systems. Also, if our web app is installed as a PWA, it has standalone rights, so their lifecycle is similar to native apps.&lt;/p&gt;
&lt;h2 id=&quot;understanding-workers-and-service-workers&quot;&gt;Understanding Workers and Service Workers &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#understanding-workers-and-service-workers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A Web Worker is a JavaScript script that runs in the background, independent of the main thread of a web application, but attached to it. This allows developers to perform computationally expensive tasks without blocking the user interface. Web Workers do not have access to the DOM but can communicate with the main thread through message passing, ensuring smooth and responsive user experiences for web applications. Sometimes developers think that if a code is running in a new thread, it&#39;s then applicable to the idea of running in the background, but actually a Web Worker typically follows the state of the main web application context. Therefore, if your web app is suspended in the background, the same will happen to their web workers.&lt;/p&gt;
&lt;p&gt;Browsers and web runtimes today also support a different kind of thread, a Service Worker.&lt;br /&gt;
Service Workers operate differently from web workers. While web workers are tied to the lifecycle of a page, service workers persist independently, so they are good candidates to store code that we want to execute when the main web application is not active and not executing any code. However, browsers may pause or terminate service workers if all the clients from associated pages are inactive for an extended period. Also, browsers limit when your service worker can be woke up to execute code.&lt;br /&gt;
Service workers excel at handling tasks like caching, push notifications, and background syncs. Understanding their limitations and capabilities is essential for maximizing their potential.&lt;/p&gt;
&lt;h2 id=&quot;execution-rights-while-in-the-background-on-every-state&quot;&gt;Execution Rights while in the background on every state &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#execution-rights-while-in-the-background-on-every-state&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We&#39;ve already stated that on different scenarios and different operating systems and browsers, execution rights in the background might differ, but let&#39;s asume that we won&#39;t have rights. What we can do to overpass that situation and execute code while in the background? Today we have several options:&lt;/p&gt;
&lt;p&gt;1- Publish the web app in app stores&lt;/p&gt;
&lt;p&gt;It&#39;s possible to create a PWA launcher to publish in Google Play Store, Amazon App Store, Huawei App Gallery and Apple AppStore. Because these launchers or packages are native, they may overpass the limitations of the browsers in terms of background execution.&lt;/p&gt;
&lt;p&gt;2- Media playing&lt;/p&gt;
&lt;p&gt;Using the right browser APIs, such as Media Session and Picture and Picture APIs, our web app will be able to run code while providing video and audio data to keep it playing in the background&lt;/p&gt;
&lt;p&gt;3- Through Service Workers&lt;/p&gt;
&lt;p&gt;Because Service Workers have their own lifecycle, separated from the main web app lifecycle, they may execute code, within certain restricted limitations, even if the main web app context is suspended, frozen or even if it was terminated.&lt;/p&gt;
&lt;h2 id=&quot;some-use-cases-and-ideas&quot;&gt;Some use cases and ideas &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#some-use-cases-and-ideas&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;1- Network Requests: If your web app goes to the background while anetwork request is in process, it may be aborted.&lt;br /&gt;
Solutions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Beacon API, if suitable&lt;/li&gt;
&lt;li&gt;Web Background Synchronization API&lt;/li&gt;
&lt;li&gt;Background Fetch API&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;2- Sync Data: Applications that sync data periodically might need to delay syncs when moved to the background. You can utilize Web Background Synchronization and Periodic Background Sync to ensure data is synchronized once the app regains focus or a network becomes available.&lt;/p&gt;
&lt;p&gt;3- Notify the User: Use Web Notifications and Web Push APIs to keep users informed about important updates even when the app is not active. These notifications can help re-engage users and ensure they do not miss critical events.&lt;/p&gt;
&lt;p&gt;4- Continue pending tasks: Tasks like downloading large files or processing data can be completed in the background using Service Workers or Background Fetch APIs. This ensures continuity without requiring active user interaction.&lt;/p&gt;
&lt;h2 id=&quot;apis-for-managing-background-javascript&quot;&gt;APIs for Managing Background JavaScript &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#apis-for-managing-background-javascript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;page-visibility-api&quot;&gt;Page Visibility API &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#page-visibility-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use the visibilitychange event to detect when a tab or a standalone web app window becomes hidden or visible:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;visibilitychange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;       console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Tab is in the background&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token comment&quot;&gt;// Pause or throttle tasks&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;       console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Tab is in the foreground&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token comment&quot;&gt;// Resume tasks&lt;/span&gt;&lt;br /&gt;   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Monitoring visibility changes allows developers to adapt application behavior dynamically, conserving resources when necessary.&lt;/p&gt;
&lt;h3 id=&quot;page-lifecycle-api&quot;&gt;Page Lifecycle API &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#page-lifecycle-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Page Lifecycle API builds upon the limitations of the Page Visibility API by providing detailed events and properties to manage the states of a web app, such as being frozen or discarded. This API helps developers understand and respond to different lifecycle states of a page, optimizing resource usage and enhancing the user experience.&lt;/p&gt;
&lt;p&gt;Here’s a simple example using the PageLifecycle API:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Listen for &#39;freeze&#39; and &#39;resume&#39; events&lt;/span&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;freeze&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Page is frozen. Save state here.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;resume&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Page has resumed. &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Check if the page was discarded&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wasDiscarded&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Page was discarded by the browser. Reloading data...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This snippet demonstrates handling freeze and resume events, and checking the document.wasDiscarded property to respond to browser-induced page discards, ensuring a seamless experience for users.&lt;/p&gt;
&lt;h3 id=&quot;beacon-api&quot;&gt;Beacon API &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#beacon-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Beacon API can be used to send network requests that you don&#39;t care about its response. Because of that nature, the browser can finish the request and sending the data, even if there is no more JavaScript code to receive the answer, such as when your web app is not active anymore.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;visibilitychange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;visibilityState &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;hidden&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendBeacon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/log-hidden&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; someData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;web-background-synchronization&quot;&gt;Web Background Synchronization &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#web-background-synchronization&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Background Sync API, currently supported only by Chromium-based browsers, allows you to mark pending synchronization tasks from your main web application. These tasks are identified by a custom name, such as &amp;quot;data-sync&amp;quot; or &amp;quot;save record.&amp;quot; When the browser is ready, it will activate a registered service worker and execute the &amp;quot;sync&amp;quot; event handler. This handler will receive the task name as an argument, enabling you to connect to your server and complete the pending operation.&lt;/p&gt;
&lt;p&gt;Since this process occurs within the service worker context, it operates independently of the web application, allowing synchronization to happen in the background. However, it&#39;s crucial to remember that you should implement a fallback mechanism for browsers that don&#39;t support this API.&lt;/p&gt;
&lt;h3 id=&quot;periodic-background-sync&quot;&gt;Periodic Background Sync &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#periodic-background-sync&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Periodic Background Sync is a feature that allows you to schedule synchronization tasks to run at regular intervals. This can be useful for ensuring that data is synchronized even if the user is not actively using your app.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;periodicsync&lt;/code&gt; event will be fired in the Service Worker:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On a synchronization time interval&lt;/li&gt;
&lt;li&gt;If battery and network conditions are met&lt;/li&gt;
&lt;li&gt;It&#39;s not mandatory to access the network on each execution&lt;/li&gt;
&lt;li&gt;Right now, it&#39;s fired with a maximum of once every 12 hours&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It works only on Chromium-based browsers.&lt;/p&gt;
&lt;h3 id=&quot;background-fetch&quot;&gt;Background Fetch &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#background-fetch&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Background Fetch lets the webapp download files in the background managing events, such as progress, error or finished in the service worker context. While these files are downloaded, the browsers typically creates a notification so the user knows this operation is happening. These files are then typically stored locally using the FileSystem API to be used later when the user goes back to the web app.&lt;br /&gt;
This API is available on Chromium-based browsers and it&#39;s available as an experiment in Safari at the beginning of 2025.&lt;/p&gt;
&lt;h3 id=&quot;web-push-notifications&quot;&gt;Web Push Notifications &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#web-push-notifications&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With this API you will be able to notify the user with an OS notification from a Service Worker, that can be triggered at any time you have execution rights. The most common scenario is to create that notification when you send a push message from your server using the Web Push architecture available today on every browser.&lt;/p&gt;
&lt;p&gt;With Apple’s recent adoption of Web Push in Safari (full support on macOS, only standalone webapps on iOS and iPadOS), developers can now engage users more effectively on all devices.&lt;br /&gt;
This development revitalizes interest in web push technologies, making it easier to communicate with users even when apps are not active.&lt;/p&gt;
&lt;h2 id=&quot;challenges-in-background-execution&quot;&gt;Challenges in Background Execution &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#challenges-in-background-execution&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;timers-and-queues&quot;&gt;Timers and Queues &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#timers-and-queues&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When a tab moves to the background, browsers often reduce the frequency of timer callbacks to save resources and, as we mentioned before, it may even stopped them. For instance, a setInterval callback scheduled every 10 milliseconds might execute once per second in the background. This behavior varies by browser:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chrome: Reduces timer frequency significantly when a tab is hidden.&lt;/li&gt;
&lt;li&gt;Firefox: Behaves similarly but may also pause timers when windows are covered on desktop.&lt;/li&gt;
&lt;li&gt;Safari: Takes an aggressive approach, pausing almost all background activity to conserve battery life.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The unpredictability of timers can disrupt critical functionalities, such as data syncing, animations, or UI updates, requiring developers to implement robust fallbacks.&lt;/p&gt;
&lt;h3 id=&quot;animations-and-frame-rates&quot;&gt;Animations and Frame Rates &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#animations-and-frame-rates&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;APIs like &lt;code&gt;requestAnimationFrame&lt;/code&gt; stop triggering callbacks when the page is hidden. Similarly, CSS animations and transitions cease execution. This behavior ensures that background tabs do not drain unnecessary resources, but it can also create challenges for applications relying on smooth visual transitions.&lt;/p&gt;
&lt;h3 id=&quot;detecting-time-passed&quot;&gt;Detecting time passed &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#detecting-time-passed&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To keep the session consistent, it&#39;s always a good idea to detect when the user comes from a possible inactive or suspended state and check how much time has passed since the last time it was active. If that time distance is greater than a threshold you define, you may want to refresh, reload data, or re-authorize with your server to avoid any user experience issues.&lt;/p&gt;
&lt;p&gt;Here’s a simple example in JavaScript to detect inactivity and handle it:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lastActiveTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; threshold &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 30 minutes in milliseconds&lt;/span&gt;&lt;br /&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;visibilitychange&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; currentTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; timeElapsed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; currentTime &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; lastActiveTime&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;timeElapsed &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; threshold&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Session expired. Refreshing data...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// Perform your refresh or re-authorization logic here&lt;/span&gt;&lt;br /&gt;      location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reload&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Welcome back!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    lastActiveTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;tools-and-techniques-for-debugging-background-behavior&quot;&gt;Tools and Techniques for Debugging Background Behavior &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#tools-and-techniques-for-debugging-background-behavior&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Understanding what happens in the background requires specialized tools and techniques:&lt;/p&gt;
&lt;p&gt;Most modern browsers provide detailed insights into service workers, memory usage, and performance metrics. Chromium DevTools, such as the ones available in Google Chrome or Microsoft Edge are miles away from WebKit and Firefox devtools when talking about background execution.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/understanding-js-background/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Understanding JavaScript&#39;s behavior in the background is essential for optimizing web apps and ensuring a seamless user experience. This article has explored how modern browsers and operating systems handle JavaScript execution when an app moves to the background. Concepts like background states, service workers, and APIs such as Page Visibility and Background Sync help developers tackle common challenges like paused timers, suspended animations, and resource constraints. By leveraging these tools, developers can design applications that adapt smoothly to interruptions.&lt;/p&gt;
&lt;p&gt;Background execution affects user satisfaction, resource efficiency, and app reliability. Ensuring smooth transitions when resuming paused apps, managing data synchronization, and delivering timely notifications are key to maintaining functionality. With tools like browser devtools and APIs, developers can create apps that perform consistently, whether in active use or operating in the background, across various devices and platforms.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: This article was published at &lt;a href=&quot;https://devm.io/reader/reading/ijs-magazine/Volume%2017/c286bfd508923c1411fed27d&quot;&gt;iJS Magazine for Devmio&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Vanilla Web </title>
		<link href="https://firt.dev/vanilla-web/"/>
		<updated>2025-05-16T00:00:00Z</updated>
		<id>https://firt.dev/vanilla-web/</id>
		<description>
		
			A session available in English and Spanish about the usage of the web core technologies without frameworks
				
		</description>
		<content type="html">&lt;p&gt;During 2024 and 2025 I delivered the talk &amp;quot;Vanilla Web, you don&#39;t need that library&amp;quot; a couple of times. Here you can find three versions:&lt;/p&gt;
&lt;h2 id=&quot;in-english-delivered-at-ndc-in-oslo-%F0%9F%87%B3%F0%9F%87%B4&quot;&gt;In English delivered at NDC in Oslo 🇳🇴 &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/vanilla-web/#in-english-delivered-at-ndc-in-oslo-%F0%9F%87%B3%F0%9F%87%B4&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=GEbDlvrdSQk&amp;amp;ab_channel=NDCConferences&quot;&gt;https://www.youtube.com/watch?v=GEbDlvrdSQk&amp;amp;ab_channel=NDCConferences&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;in-english-delivered-at-devoxx-uk-in-london-%F0%9F%87%AC%F0%9F%87%A7&quot;&gt;In English delivered at Devoxx UK in London 🇬🇧 &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/vanilla-web/#in-english-delivered-at-devoxx-uk-in-london-%F0%9F%87%AC%F0%9F%87%A7&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=pG9jvlNQzMU&amp;amp;ab_channel=DevoxxUK&quot;&gt;https://www.youtube.com/watch?v=pG9jvlNQzMU&amp;amp;ab_channel=DevoxxUK&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;in-spanish-delivered-at-nerdearla-in-buenos-aires-%F0%9F%87%A6%F0%9F%87%B7&quot;&gt;In Spanish delivered at Nerdearla in Buenos Aires 🇦🇷 &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/vanilla-web/#in-spanish-delivered-at-nerdearla-in-buenos-aires-%F0%9F%87%A6%F0%9F%87%B7&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=pxFkPSr98SQ&amp;amp;ab_channel=Nerdearla&quot;&gt;https://www.youtube.com/watch?v=pxFkPSr98SQ&amp;amp;ab_channel=Nerdearla&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;in-english-at-utahjs-in-salt-lake-city-%F0%9F%87%BA%F0%9F%87%B8&quot;&gt;In English at UtahJS in Salt Lake City 🇺🇸 &lt;a class=&quot;direct-link&quot; href=&quot;https://firt.dev/vanilla-web/#in-english-at-utahjs-in-salt-lake-city-%F0%9F%87%BA%F0%9F%87%B8&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=V2eM0EdTfmk&amp;amp;pp=ygUHZmlydG1hbg%3D%3D&quot;&gt;https://www.youtube.com/watch?v=V2eM0EdTfmk&amp;amp;pp=ygUHZmlydG1hbg%3D%3D&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;⁠&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Unlocking Generative AI in your Web App </title>
		<link href="https://firt.dev/genai-web-apps/"/>
		<updated>2025-05-16T00:00:00Z</updated>
		<id>https://firt.dev/genai-web-apps/</id>
		<description>
		
			
				
		</description>
		<content type="html">&lt;p&gt;During Devoxx UK in London 🇬🇧 I delivered this talk&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=pG4FvMSu_C0&amp;amp;ab_channel=DevoxxUK&quot;&gt;https://www.youtube.com/watch?v=pG4FvMSu_C0&amp;amp;ab_channel=DevoxxUK&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Discover how to seamlessly integrate AI LLM models into your website using cutting-edge techniques like new client-side APIs and cloud services. Learn how to execute AI models in the front-end without incurring cloud fees by leveraging Chrome&#39;s Gemini Nano model using the &lt;a href=&quot;http://window.ai/&quot;&gt;window.ai&lt;/a&gt; inference API, or utilizing WebNN, WebGPU, and WebAssembly for open-source models.&lt;/p&gt;
&lt;p&gt;This session dives into API integration, token management, secure prompting, and practical demos to get you started with AI on the web.&lt;/p&gt;
&lt;p&gt;Unlock the power of AI on the web while having fun along the way!&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Your First Web Component </title>
		<link href="https://firt.dev/first-web-component/"/>
		<updated>2025-04-11T00:00:00Z</updated>
		<id>https://firt.dev/first-web-component/</id>
		<description>
		
			Vanilla reusable components Tutorial
				
		</description>
		<content type="html">&lt;figure&gt;&lt;img src=&quot;https://firt.imgix.net/img/courses/fm-tutorial-web-component.jpg?auto=format&quot; alt=&quot;&quot; /&gt;&lt;figcaption&gt;Available for Frontend Masters subscribers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Learn the fundamentals of Web Components and how to create your own from scratch using JavaScript. Define custom elements using HTML elements, attach shadow DOM, and encapsulate styles and logic for reusable components.Build, register, and use your first Web Component without frameworks, and understand where they fit in modern web apps.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aPi4b49lx80&quot;&gt;https://www.youtube.com/watch?v=aPi4b49lx80&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=aPi4b49lx80&quot; class=&quot;button&quot; target=&quot;_blank&quot;&gt;Open in YouTube&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://frontendmasters.com/tutorials/firt/your-first-web-component/?code=firtman&amp;utm_source=firtman&amp;utm_medium=social&amp;utm_campaign=teacher_coupon&quot; class=&quot;button&quot; target=&quot;_blank&quot;&gt;See the tutorial &lt;br /&gt;$10 OFF First Month&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also request a live custom workshop for you or your team on the topics of this course. &lt;a href=&quot;https://firt.dev/about&quot;&gt;Let&#39;s keep in touch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
	</entry>
	
	<entry>
		<title>iPad Naming &quot;Consistency&quot; </title>
		<link href="https://firt.dev/notes/ipad/"/>
		<updated>2025-03-04T00:00:00Z</updated>
		<id>https://firt.dev/notes/ipad/</id>
		<description>
		
			A review on how Apple named different iPads over time
				
		</description>
		<content type="html">&lt;ul&gt;
&lt;li&gt;iPad&lt;/li&gt;
&lt;li&gt;iPad 2&lt;/li&gt;
&lt;li&gt;New iPad — later iPad 3th Generation&lt;/li&gt;
&lt;li&gt;iPad 4th Generation&lt;/li&gt;
&lt;li&gt;iPad 5th generation&lt;/li&gt;
&lt;li&gt;iPad Air&lt;/li&gt;
&lt;li&gt;iPad Air 2&lt;/li&gt;
&lt;li&gt;iPad Mini&lt;/li&gt;
&lt;li&gt;iPad Mini 2&lt;/li&gt;
&lt;li&gt;iPad Mini 3&lt;/li&gt;
&lt;li&gt;iPad Mini 4&lt;/li&gt;
&lt;li&gt;12.9&amp;quot; iPad Pro&lt;/li&gt;
&lt;li&gt;12.9&amp;quot; iPad Pro 2nd Generation&lt;/li&gt;
&lt;li&gt;12.9&amp;quot; iPad Pro 3rd Generation&lt;/li&gt;
&lt;li&gt;9.7&amp;quot; iPad Pro&lt;/li&gt;
&lt;li&gt;10.5&amp;quot; iPad Pro&lt;/li&gt;
&lt;li&gt;11&amp;quot; iPad Pro&lt;/li&gt;
&lt;li&gt;iPad (9.7&amp;quot;) — later iPad 6th Generation&lt;/li&gt;
&lt;li&gt;iPad Air — later iPad Air 3rd Generation&lt;/li&gt;
&lt;li&gt;iPad Mini — later 5th Generation&lt;/li&gt;
&lt;li&gt;iPad Pro 12.9&amp;quot; 4th Generation&lt;/li&gt;
&lt;li&gt;iPad Pro 11&amp;quot; 2nd Generation&lt;/li&gt;
&lt;li&gt;iPad 7th Generation&lt;/li&gt;
&lt;li&gt;iPad 8th Generation&lt;/li&gt;
&lt;li&gt;iPad Air - later iPad Air 4th Generation&lt;/li&gt;
&lt;li&gt;iPad Pro 11 3rd Generation (M1)&lt;/li&gt;
&lt;li&gt;iPad Pro 12.9 5th Generation (M1)&lt;/li&gt;
&lt;li&gt;iPad (9th Generation)&lt;/li&gt;
&lt;li&gt;iPad mini (6th Generation)&lt;/li&gt;
&lt;li&gt;iPad Air 5th Generation&lt;/li&gt;
&lt;li&gt;iPad (10th Generation)&lt;/li&gt;
&lt;li&gt;iPad Pro 12.9 6th Generation&lt;/li&gt;
&lt;li&gt;iPad Pro 11-in 4th Generation&lt;/li&gt;
&lt;li&gt;iPad Air 11-in (M2)&lt;/li&gt;
&lt;li&gt;iPad Air 13-in (M2)&lt;/li&gt;
&lt;li&gt;iPad Pro 11-in (M4)&lt;/li&gt;
&lt;li&gt;iPad Pro 13-in (M4)&lt;/li&gt;
&lt;li&gt;iPad mini (A17 Pro) 🆕&lt;/li&gt;
&lt;li&gt;iPad A16 🆕&lt;/li&gt;
&lt;li&gt;iPad Air 11-in (M3) 🆕&lt;/li&gt;
&lt;li&gt;iPad Air 13-in (M3) 🆕&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>Promesas en JavaScript </title>
		<link href="https://firt.dev/promesas-js/"/>
		<updated>2025-03-01T00:00:00Z</updated>
		<id>https://firt.dev/promesas-js/</id>
		<description>
		
			A short course in Spanish on JS Promises
				
		</description>
		<content type="html">&lt;figure&gt;&lt;img src=&quot;https://firt.imgix.net/img/courses/midu-promesas.webp?auto=format&quot; alt=&quot;&quot; /&gt;&lt;figcaption&gt;Available for Frontend Masters subscribers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;This course is available only in Spanish and it&#39;s on JavaScript Promises.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Aprende y domina las promesas en JavaScript para trabajar con asincronía y manejar errores de forma efectiva. Desde 0 hasta métodos avanzados como Promise.all y Promise.race, y cómo combinar las promesas con async/await&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://midu.dev/curso/promesas-javascript&quot; class=&quot;button&quot; target=&quot;_blank&quot;&gt;See course (in Spanish)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;En este curso de 40 minutos verás:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Introducción a las promesas y su sintaxis&lt;/li&gt;
&lt;li&gt;Encadenamiento de promesas (then, catch, finally)&lt;/li&gt;
&lt;li&gt;Resolviendo múltiples promesas con Promise.all y Promise.race&lt;/li&gt;
&lt;li&gt;Diferencias entre callbacks y promesas&lt;/li&gt;
&lt;li&gt;async/await&lt;/li&gt;
&lt;li&gt;Buenas prácticas para manejar promesas y errores&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also request a live custom workshop for you or your team on the topics of this course. &lt;a href=&quot;https://firt.dev/about&quot;&gt;Let&#39;s keep in touch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
	</entry>
</feed>
