{"id":4910,"date":"2023-02-14T14:04:26","date_gmt":"2023-02-14T19:04:26","guid":{"rendered":"https:\/\/ionic.io\/blog\/?p=4910"},"modified":"2023-02-15T14:51:28","modified_gmt":"2023-02-15T19:51:28","slug":"pwas-using-sveltekit-and-ionic","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic","title":{"rendered":"Blazing fast PWAs with SEO power using SvelteKit and Ionic"},"content":{"rendered":"\n<div class=\"wp-block-jetpack-markdown\"><blockquote>\n<p>This is a guest post from Ionic Developer Expert Tom Gruintjes. While Ionic Framework does not officially support Svelte today, Tom created the <code>ionic-svelte<\/code> package, which uses Ionic Framework\u2019s web components to create an out-of-the-box integration with Svelte. Continue reading to learn how you can use Svelte with Ionic Framework\u2019s UI components.<\/p>\n<\/blockquote>\n<p>In the world of web development, one year is almost a lifetime. So many things happen \u2013 APIs change with a blink of an eye, new frameworks come out, and for Svelte \u2013 SvelteKit 1.0 was finally released. SvelteKit is a so-called \u2018meta-framework\u2019. On top of Svelte, it provides tools, features, and opinions on how to organize your code for the client and the server. It also provides a router and all sorts of additional goodies to supercharge any web app.<\/p>\n<p>SvelteKit projects can deliver blazing fast web-apps that support SEO out of the box. This blog aims to show you how to set up a fast, SEO-optimized landing page that allows users to install a PWA, optimized for performance by Lighthouse standards, utilizing SvelteKit and Ionic\u2019s UI components made capable by ionic-svelte.<\/p>\n<\/div>\n\n\n\n<!--more-->\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><h2>Step 1 \u2013 Let\u2019s set up the Ionic SvelteKit project base project<\/h2>\n<p>Here we like to use the create command of the ionic-svelte repo as follows:<\/p>\n<pre><code> npm create ionic-svelte-app@latest ionic-sveltekit-ssr-demo\n<\/code><\/pre>\n<p>You\u2019ll see a new Ionic-Kit project spun up with the configurations and integrations all set:<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"525\" height=\"114\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-setup.png.png\" alt=\"\" class=\"wp-image-4912 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-setup.png.png 525w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-setup.png-300x65.png 300w\" data-sizes=\"auto, (max-width: 525px) 100vw, 525px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 525px; --smush-placeholder-aspect-ratio: 525\/114;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"525\" height=\"114\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-setup.png.png\" alt=\"\" class=\"wp-image-4912\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-setup.png.png 525w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-setup.png-300x65.png 300w\" sizes=\"auto, (max-width: 525px) 100vw, 525px\" \/><\/noscript><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>Under the hood, this runs SvelteKit\u2019s create command and spawns npm commands to get Ionic\u2019s goodies in the project. Next, some configurations are done in svelte.config.js, tsconfig.json and package.json. As a plus \u2013 we add Capacitor integration dependencies, so our app can use its plugin ecosystem as PWA, as well as deploy as an Android\/iOS app.<\/p>\n<p>This command also sets our adapter configuration to <code>static<\/code>, meaning we will publish using static hosting (HTML, JS, CSS plus assets on Firebase Hosting, etc.). So, we opt out of managing API REST endpoints via SvelteKit in the same codebase. If we need that, switch to adapter <code>auto<\/code>.<\/p>\n<p>Next \u2013 we set up Tailwind to support the styling of the landing page. Here you can follow the guide from the <a href=\"https:\/\/tailwindcss.com\/docs\/guides\/sveltekit\">Tailwind site<\/a> or you can use the <a href=\"https:\/\/github.com\/svelte-add\/tailwindcss\">instructions on GitHub<\/a>. I used the latter and tweaked a few settings and references:<\/p>\n<\/div>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npx svelte-add@latest tailwindcss<\/code><\/pre>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>We also set up the service worker. We deviate from the seemingly simple explanation of the Vite PWA Kit plugin &#8211; it did not work for us in combination with Vercel. And for those who don\u2019t know &#8211; service workers also help compensate for low connectivity and flaky connections \u2013 and even offline experience for those app parts that can be used offline. How we did it &#8211; see app.html, vite.config.js (in the repo linked below) and run the following command:<\/p>\n<\/div>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm i -D workbox-window vite-plugin-pwa<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Steps 2 \u2013 Preparing routes and configure proper rendering patterns<\/h2>\n\n\n\n<p>Out of the box, we have Ionic loaded at the root route (see <code>src\/+layout.svelte<\/code>) and ssr disabled for the whole app (see <code>src\/+layout.ts<\/code>). We don\u2019t want that, so we create a subroute for the PWA holding the Ionic UI goodies. And a subroute for the landing page:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Create folder <code>app<\/code> under root route<\/li>\n\n\n\n<li>Copy +layout.* and +page.* from root to that folder<\/li>\n\n\n\n<li>Change path to theme variables in route<code>\/app\/+layout.svelte<\/code> (<code>import &#039;..\/..\/theme\/variables.css&#039;;<\/code>)<\/li>\n<\/ol>\n\n\n\n<p>Then we configure <code>ssr = true<\/code> for the whole app, except the app routes:<\/p>\n\n\n\n<p>In <a href=\"https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/%2Blayout.ts\">https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/%2Blayout.ts<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">export const ssr = true<\/code><\/pre>\n\n\n\n<p>And in <a href=\"https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/app\/%2Blayout.ts\">https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/app\/%2Blayout.ts<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">export const ssr = false<\/code><\/pre>\n\n\n\n<p>Next, we need to remove all Ionic references in the routes that have <code>ssr = true<\/code>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>+layout.svelte<\/code> \u2013 remove everything except for the <code>app.postcss<\/code> (tailwind integration) and <code>&lt;slot\/&gt;<\/code> &#8211; we don\u2019t want the Ionic setup routine at root level<\/li>\n\n\n\n<li><code>+page.svelte<\/code> \u2013 remove all and replace with \u201cHello world!\u201d \u2013 we\u2019ll put the Landing page code here later<\/li>\n<\/ul>\n\n\n\n<p>Now we have set up the proper web rendering patterns for each route: SSR for our landing page and SPA setup for the PWA, all in one code-base.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3 \u2013 Creating the SEO powered landing page<\/h2>\n\n\n\n<p>Here we use a Tailwind+HTML starter template from the web. We opt for a static page, but of course you are free to use a more dynamic way using SvelteKit starters etc. CMS integration is also possible.<\/p>\n\n\n\n<p>We have split the landing page parts in various components, so the landing page structure looks like this (see <a href=\"https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/lib\/components\/Landingpage\/Landing.svelte\">https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/lib\/components\/Landingpage\/Landing.svelte<\/a>):<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;script&gt;\n\timport FAQ from &#039;$lib\/components\/Landingpage\/FAQ.svelte&#039;;\n\timport Features from &#039;$lib\/components\/Landingpage\/Features.svelte&#039;;\n\timport Footer from &#039;$lib\/components\/Landingpage\/Footer.svelte&#039;;\n\timport Hero from &#039;$lib\/components\/Landingpage\/Hero.svelte&#039;;\n\timport HowItWorks from &#039;$lib\/components\/Landingpage\/HowItWorks.svelte&#039;;\n\timport Pricing from &#039;$lib\/components\/Landingpage\/Pricing.svelte&#039;;\n&lt;\/script&gt;\n\n&lt;!-- Hero --&gt;\n&lt;Hero \/&gt;\n\n&lt;!-- How it works --&gt;\n&lt;HowItWorks \/&gt;\n\n&lt;!-- Features --&gt;\n&lt;Features \/&gt;\n\n&lt;!-- Pricing --&gt;\n&lt;Pricing \/&gt;\n\n&lt;!-- FAQ  --&gt;\n&lt;FAQ \/&gt;\n\n&lt;!-- Footer --&gt;\n&lt;Footer \/&gt;<\/code><\/pre>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>We added a simple button handler in &lt;Hero&gt; which captures the click for the CTA which navigates to the PWA.<\/p>\n<p>In: <a href=\"https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/lib\/components\/Landingpage\/Hero.svelte\">https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/lib\/components\/Landingpage\/Hero.svelte<\/a>:<\/p>\n<\/div>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;script&gt;\n\timport { goto } from &#039;$app\/navigation&#039;;\n\tconst goToApp = () =&gt; {\n\t\tgoto(&#039;\/app\/install&#039;);\n\t};\n&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p>and<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;button on:click={goToApp}&gt;Install app&lt;\/button&gt;<\/code><\/pre>\n\n\n\n<p>P.S. Do you see the amazing simple syntax of Svelte?!<\/p>\n\n\n\n<p>And connect <code>Landing.svelte<\/code> component in <code>routes\/+page.svelte<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;script lang=&quot;ts&quot;&gt;\n\timport Landing from &#039;$lib\/components\/Landingpage\/Landing.svelte&#039;;\n&lt;\/script&gt;\n&lt;Landing\/&gt;<\/code><\/pre>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p><a href=\"https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/%2Bpage.svelte\">https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/%2Bpage.svelte<\/a><\/p>\n<p>P.S. Again you can see the powerful and easy syntax of Svelte!<\/p>\n<p>Now we can spin the app \u2013 <code>npm run dev<\/code>.  Press o to open the browser so you can see the result so far:  two working routes &#8211;<code> \/<\/code> and <code>\/app<\/code>.  Do you notice how fast Vite boots?!<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><h2>Step 4 &#8211; Configure the routing flows pending installation state of app<\/h2>\n<p>As we are serving two different appearances of our app on the same domain, we need to include some logic to handle the routing:<\/p>\n<ul>\n<li>When installed as PWA \u2013 the landing page should be skipped<\/li>\n<li>Otherwise, show the landing page (in later stage maybe even protect the PWA routes)<\/li>\n<\/ul>\n<p>Here, I choose to do this handling on the client\u2019s side for multiple reasons. We could optimize further into SSR \u2013 but we always need the window object to determine if we are in PWA state &#8211; so here we are paying a small price at one point. onMount is the lifecycle hook we will use to access the <code>window<\/code>  object safely and the right path is navigated to.<\/p>\n<\/div>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;script lang=&quot;ts&quot;&gt;\n\timport { goto } from &#039;$app\/navigation&#039;;\n\timport Landing from &#039;$lib\/components\/Landingpage\/Landing.svelte&#039;;\n\timport { onMount } from &#039;svelte&#039;;\n\timport { Capacitor } from &#039;@capacitor\/core&#039;;\n\tconst isPWA = (win: Window): boolean =&gt;\n\t\t!!(win.matchMedia?.(&#039;(display-mode: standalone)&#039;).matches || \n                (win.navigator as any).standalone);\n\tlet showLanding = false;\n\tonMount(() =&gt; {\n\t\tconsole.log(&#039;Are we native?&#039;, Capacitor.isNativePlatform());\n\t\tif (Capacitor.isNativePlatform()) {\n\t\t\tconsole.log(&#039;Found native shell, redirecting&#039;);\n\t\t\tgoto(&#039;\/app\/login&#039;);\n\t\t\treturn;\n\t\t}\n\t\tif (isPWA(window)) {\n\t\t\tconsole.log(&#039;In PWA - on wrong route - redirecting&#039;);\n\t\t\tgoto(&#039;\/app\/splash&#039;);\n\t\t} else showLanding = !isPWA(window);\n\t});\n&lt;\/script&gt;\n{#if showLanding}\n\t&lt;Landing \/&gt;\n{\/if}<\/code><\/pre>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>This logic uses the code from Ionic\u2019s platform API. We cannot import this directly from ionic-svelte as we will get a 500 error. Next, to avoid flicker going to the PWA in Capacitor (or a wrong path in PWA), we also need to conditionally render the Landing page. This weakens the \u201cfull SSR\u201d claim as we do our checks after the client is fully mounted (aka AfterViewInit in Angular)\u2013 Again, a small price, and in our case, no big deal.<\/p>\n<p>To optimize further we set <code>start_url: &quot;\/app\/splash&quot;<\/code> in the PWA manifest.  <a href=\"https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/vite.config.js\">https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/vite.config.js<\/a><\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><h2>Step 5 \u2013 Performance boosting the the SPA<\/h2>\n<p>We need to assure the SPA gets its maximum power by pre-loading and pre-caching it. We can do this while the user is looking at the landing page, signing up and\/or reading the T&amp;C etc. (not implemented in the repo). For this, we:<\/p>\n<ul>\n<li>Use the service worker to run from the root route \u2013 even though we really need it at \/app route<\/li>\n<li>Configure SvelteKit to prefetch all code once the SPA is invoked \u2013 basically prefetching all routes, instead of lazy loading. We cannot do this in the landing page, as it will load Ionic in SSR (500 error)<\/li>\n<\/ul>\n<p>The prefetching happens through a call to preloadCode() when not in dev mode. Putting this in <code>+layout.svelte<\/code> means \u2013 the moment any route in the PWA is called, it will prefetch the code of the whole app.  We don\u2019t want this in dev-mode.<\/p>\n<p><a href=\"https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/app\/%2Blayout.svelte\">https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/app\/%2Blayout.svelte<\/a><\/p>\n<\/div>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">\/\/ Prefetch all code when not in dev\nif (!dev) preloadCode();<\/code><\/pre>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>And when this code is in the service worker cache, it will get this code from the cache \u2013 creating a huge performance boost beyond the first run. Again, we can only run preloadCode() in the Ionic enabled routes. Otherwise, we get a 500 error.<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><h2>Step 6 \u2013 Adding some install, signup, T&amp;C and splash goodies, etc\u2026<\/h2>\n<p>Just for fun I added a splash page for the PWA, the install route and a mock-login route. The final repo has four routes:<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"232\" height=\"143\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-directory.png\" alt=\"\" class=\"wp-image-4911 lazyload\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 232px; --smush-placeholder-aspect-ratio: 232\/143;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"232\" height=\"143\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-directory.png\" alt=\"\" class=\"wp-image-4911\"\/><\/noscript><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><ul>\n<li><code>\/route<\/code> &#8211; main entry point<\/li>\n<li><code>\/app<\/code> &#8211; should lead to redirect to splash<\/li>\n<li><code>\/install<\/code>  &#8211; showing the installation UI and logic<\/li>\n<li><code>\/splash<\/code> &#8211; splash page (optional)<\/li>\n<li><code>\/login<\/code> &#8211; dummy login\/signup page<\/li>\n<\/ul>\n<p>Some important points to note:<\/p>\n<ul>\n<li>For installation on Android phones, we experienced issues with the Samsung browser. So we need to cover for that and instruct the user to use a different browser for installing the app. Also, we must think through carefully instructing users how to launch the PWA (which could be a full blog on its own if you ask me)!<\/li>\n<li>The iOS installation routine needs to be done manually<\/li>\n<li>In our project, we needed Firebase-push notifications through the web SDK. This requires an additional service worker to be installed with a designated name. We placed that one under route scope \/app and initialized it manually via code in app.html. A bit of a pain, but it works.<\/li>\n<\/ul>\n<p><strong>End result \u2013 pretty neat lighthouse scores and good performance on test devices<\/strong><\/p>\n<p>It already became very apparent that this setup performs amazingly fast on mobile devices with limited connectivity, and the small bundle size of SvelteKit build files really helps a lot.<\/p>\n<p>Just check out the demo app and see how fast it loads the landing page &#8211; and then do the install routine (Android Chrome, Chrome on Windows\/Mac or manual install on Safari\/iOS).<\/p>\n<p>Here is the app &#8211; <a href=\"https:\/\/ionic-svelte-ssr.web.app\">https:\/\/ionic-svelte-ssr.web.app<\/a>  and Vercel: <a href=\"https:\/\/ionic-sveltekit-ssr-demo.vercel.app\/\">https:\/\/ionic-sveltekit-ssr-demo.vercel.app\/<\/a><\/p>\n<p>See here the PWA and SEO lighthouse scores:<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"796\" height=\"528\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-lighthouse.png\" alt=\"\" class=\"wp-image-4913 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-lighthouse.png 796w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-lighthouse-300x199.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-lighthouse-768x509.png 768w\" data-sizes=\"auto, (max-width: 796px) 100vw, 796px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 796px; --smush-placeholder-aspect-ratio: 796\/528;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"796\" height=\"528\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-lighthouse.png\" alt=\"\" class=\"wp-image-4913\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-lighthouse.png 796w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-lighthouse-300x199.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-lighthouse-768x509.png 768w\" sizes=\"auto, (max-width: 796px) 100vw, 796px\" \/><\/noscript><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>Again &#8211; no strong efforts on the SEO scores &#8211; and we get sufficient hints from Lighthouse to improve it. The performance is what matters here.<\/p>\n<p>And WebPageTest using mobile and high bandwidth:<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"76\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-1024x76.png\" alt=\"\" class=\"wp-image-4914 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-1024x76.png 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-300x22.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-768x57.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-1536x113.png 1536w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth.png 1775w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/76;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"76\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-1024x76.png\" alt=\"\" class=\"wp-image-4914\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-1024x76.png 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-300x22.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-768x57.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth-1536x113.png 1536w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-high-bandwidth.png 1775w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/noscript><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>Low spec phone testing with 3G connection isn\u2019t bad either:<\/p>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"80\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-1024x80.png\" alt=\"\" class=\"wp-image-4915 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-1024x80.png 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-300x23.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-768x60.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-1536x120.png 1536w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth.png 1793w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/80;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"80\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-1024x80.png\" alt=\"\" class=\"wp-image-4915\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-1024x80.png 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-300x23.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-768x60.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth-1536x120.png 1536w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-ionic-low-bandwidth.png 1793w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/noscript><\/figure>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>We were quite happy with these scores \u2013 a big improvement while using a simple tech stack. This will pay off as we get users on-board, and there are still opportunities to further optimize.<\/p>\n<p>Of course, this app is quite basic. Then again, how big do you want to make your landing pages? By using lazy loading and service workers, it becomes quite easy to optimize the SPA.<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><h2>Bonus \u2013 adding Capacitor<\/h2>\n<p>In the repo I added a condition to check if the app actually runs in a Capacitor container (iOS\/Android), and then properly directs it to the PWA while skipping the Web Splash as we want the native Splash in these cases.<\/p>\n<p>In <a href=\"https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/%2Bpage.svelte\">https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\/blob\/main\/src\/routes\/%2Bpage.svelte<\/a>:<\/p>\n<\/div>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">onMount(() =&gt; {\n\t\tconsole.log(&#039;Are we native?&#039;, Capacitor.isNativePlatform());\n\t\tif (Capacitor.isNativePlatform()) {\n\t\t\tconsole.log(&#039;Found native shell, redirecting&#039;);\n\t\t\tgoto(&#039;\/app\/login&#039;);\n\t\t\treturn;\n\t\t}\n\n\t\tif (isPWA(window)) {\n\t\t\tconsole.log(&#039;In PWA - on wrong route - redirecting&#039;);\n\t\t\tgoto(&#039;\/app\/splash&#039;);\n\t\t} else showLanding = !isPWA(window);\n\t});<\/code><\/pre>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><p>Easy!!<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><h2>Want to know more?<\/h2>\n<ul>\n<li>Svelte.dev, Kit.svelte.dev and Learn.svelte.dev are real great resources to learn more about SvelteKit \u2013 warning: if you come from SPA background \u2013 the Kit part is a bit steep<\/li>\n<li>Ionic-svelte and the NPM package (link) \u2013 if you like to try Ionic UI in SvelteKit<\/li>\n<li>Ionic-svelte discord channel to address all your questions and comments<\/li>\n<li>Rendering patterns to maximize performance \u2013 see Fireship\u2019s blog on rendering patterns &#8211; <a href=\"https:\/\/www.youtube.com\/watch?v=Dkx5ydvtpCA\">https:\/\/www.youtube.com\/watch?v=Dkx5ydvtpCA<\/a><\/li>\n<\/ul>\n<p>To continue to improve this code, please share your findings as issues on the main repo: <a href=\"https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo\">https:\/\/github.com\/Tommertom\/ionic-sveltekit-ssr-demo<\/a><\/p>\n<p>For check out the live demo, visit <a href=\"https:\/\/ionic-svelte-ssr.web.app\">https:\/\/ionic-svelte-ssr.web.app<\/a> (Firebase-based hosted) or <a href=\"https:\/\/ionic-sveltekit-ssr-demo.vercel.app\">https:\/\/ionic-sveltekit-ssr-demo.vercel.app<\/a> (Vercel-hosted).<\/p>\n<\/div>\n\n\n\n<div class=\"wp-block-jetpack-markdown\"><h2>About the author<\/h2>\n<p><a href=\"https:\/\/www.linkedin.com\/in\/tgruintjes\/\">Tom<\/a> works as project and innovation manager at a Dutch retail bank working in various emerging markets. He is passionate about technology and more specifically front-end software development. Tom got started with Ionic and Angular 2+ back in 2016 and is now focused on <a href=\"https:\/\/github.com\/Tommertom\/svelte-ionic-app\">Svelte and Ionic<\/a>. Tom has launched the <code>ionic-svelte<\/code> package, which provides out-of-the-box integration with Ionic and Svelte \u2013 still work in progress.  Just run <code>npm create ionic-svelte-app@latest<\/code> to spin your own Ionic Svelte project!<\/p>\n<p>Tom also launched a <a href=\"https:\/\/ionicsvelte.firebaseapp.com\/\">showcase app<\/a> for Ionic elements to run from your browser and phone, with an easy source-code viewer (Vue, Angular, Vanilla, Stencil, Svelte, and React).<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":101,"featured_media":4930,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"1","publish_post_category":"12","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"559517","discourse_permalink":"http:\/\/forum.ionicframework.com\/t\/blazing-fast-pwas-with-seo-power-using-sveltekit-and-ionic\/231103","wpdc_publishing_response":"success","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[121],"tags":[23,239],"class_list":["post-4910","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering","tag-framework","tag-svelte"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Blazing fast PWAs with SEO power using SvelteKit and Ionic - Ionic Blog<\/title>\n<meta name=\"description\" content=\"Learn how to utilize SvelteKit and Ionic\u2019s UI components to deliver blazing-fast web apps that support SEO out of the box.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Blazing fast PWAs with SEO power using SvelteKit and Ionic\" \/>\n<meta property=\"og:description\" content=\"Learn how to utilize SvelteKit and Ionic\u2019s UI components to deliver blazing-fast web apps that support SEO out of the box.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-02-14T19:04:26+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-02-15T19:51:28+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2240\" \/>\n\t<meta property=\"og:image:height\" content=\"1120\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Tommertom\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Tommertom\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic\"},\"author\":{\"name\":\"Tommertom\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/14d8e401d04c4448edba9155b88779f2\"},\"headline\":\"Blazing fast PWAs with SEO power using SvelteKit and Ionic\",\"datePublished\":\"2023-02-14T19:04:26+00:00\",\"dateModified\":\"2023-02-15T19:51:28+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic\"},\"wordCount\":1948,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png\",\"keywords\":[\"Framework\",\"Svelte\"],\"articleSection\":[\"Engineering\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic\",\"url\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic\",\"name\":\"Blazing fast PWAs with SEO power using SvelteKit and Ionic - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png\",\"datePublished\":\"2023-02-14T19:04:26+00:00\",\"dateModified\":\"2023-02-15T19:51:28+00:00\",\"description\":\"Learn how to utilize SvelteKit and Ionic\u2019s UI components to deliver blazing-fast web apps that support SEO out of the box.\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png\",\"width\":2240,\"height\":1120},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Blazing fast PWAs with SEO power using SvelteKit and Ionic\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/14d8e401d04c4448edba9155b88779f2\",\"name\":\"Tommertom\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/Tom-Gruintjes-1-Tommertom-150x150.jpg\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/Tom-Gruintjes-1-Tommertom-150x150.jpg\",\"caption\":\"Tommertom\"},\"url\":\"https:\/\/ionic.io\/blog\/author\/tommertom\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Blazing fast PWAs with SEO power using SvelteKit and Ionic - Ionic Blog","description":"Learn how to utilize SvelteKit and Ionic\u2019s UI components to deliver blazing-fast web apps that support SEO out of the box.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic","og_locale":"en_US","og_type":"article","og_title":"Blazing fast PWAs with SEO power using SvelteKit and Ionic","og_description":"Learn how to utilize SvelteKit and Ionic\u2019s UI components to deliver blazing-fast web apps that support SEO out of the box.","og_url":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic","og_site_name":"Ionic Blog","article_published_time":"2023-02-14T19:04:26+00:00","article_modified_time":"2023-02-15T19:51:28+00:00","og_image":[{"width":2240,"height":1120,"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png","type":"image\/png"}],"author":"Tommertom","twitter_card":"summary_large_image","twitter_creator":"@ionicframework","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Tommertom","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic"},"author":{"name":"Tommertom","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/14d8e401d04c4448edba9155b88779f2"},"headline":"Blazing fast PWAs with SEO power using SvelteKit and Ionic","datePublished":"2023-02-14T19:04:26+00:00","dateModified":"2023-02-15T19:51:28+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic"},"wordCount":1948,"commentCount":0,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png","keywords":["Framework","Svelte"],"articleSection":["Engineering"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic","url":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic","name":"Blazing fast PWAs with SEO power using SvelteKit and Ionic - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png","datePublished":"2023-02-14T19:04:26+00:00","dateModified":"2023-02-15T19:51:28+00:00","description":"Learn how to utilize SvelteKit and Ionic\u2019s UI components to deliver blazing-fast web apps that support SEO out of the box.","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png","width":2240,"height":1120},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/pwas-using-sveltekit-and-ionic#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Blazing fast PWAs with SEO power using SvelteKit and Ionic"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/14d8e401d04c4448edba9155b88779f2","name":"Tommertom","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/Tom-Gruintjes-1-Tommertom-150x150.jpg","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/Tom-Gruintjes-1-Tommertom-150x150.jpg","caption":"Tommertom"},"url":"https:\/\/ionic.io\/blog\/author\/tommertom"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/02\/sveltekit-feature-image.png","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/4910","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/101"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=4910"}],"version-history":[{"count":12,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/4910\/revisions"}],"predecessor-version":[{"id":4939,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/4910\/revisions\/4939"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media\/4930"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=4910"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=4910"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=4910"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}