<?xml version="1.0" encoding="utf-8"?>
  <feed xmlns="http://www.w3.org/2005/Atom">
    <title>Fayez Nazzal - Blog</title>
    <link href="https://fayez.io/rss.xml" rel="self"/>
    <link href="https://fayez.io"/>
    <id>https://fayez.io/</id>
    <updated>2026-04-15T06:12:11.640Z</updated>
    <author>
      <name>Adam Sanderson</name>
    </author>
    <subtitle>Journey through software wisdom by Fayez Nazzal.</subtitle>
    <generator>JavaScript</generator>
    <entry>
        <title>clipbolt-journey</title>
        <link href="https://fayez.io/blog/clipbolt-journey/"/>
        <id>https://fayez.io/blog/clipbolt-journey/</id>
        <updated>2025-07-04T04:00:00.000Z</updated>
        <published>2025-07-04T04:00:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>I spend a considerable portion of my day copying and pasting things. Code snippets, URLs, notes, random text I find online. Like many people, I copy something, then immediately lose what I had copied before forever. This frustration kept happening all the time, so I decided to fix this.</p> <p>So I started building!</p> <h2>The Problem I Was Trying to Solve</h2> <p>Every Mac user knows this pain. You copy something important, then copy something else, and the first thing is gone forever and it will be time consuming to get it back.</p> <p>I tried other clipboard managers, but they either seem too complicated or too simple. Some had dozens of features I never used. Others looked like they were built in 2005. I wanted something that looks simple and not-disturbing while being packed with useful features under the scenes.</p> <h2>What ClipBolt Does</h2> <p>ClipBolt saves everything you copy automatically. When you need something back, you press a keyboard shortcut of your choice and see your clipboard history directly. Click what you want, it will paste directly, and the app will auto disappear.</p> <p>But I added a few things that make it more useful:</p> <ul><li><strong>Smart Organization</strong>: Instead of just showing a long list, ClipBolt groups similar items together by using AI auto-tagging.</li> <li><strong>AI Features</strong>: Sometimes you copy text that’s too long or complicated. ClipBolt can make it shorter or rewrite it in simpler words. It can also read text from images you copy using OCR. Translate text you copied. Describe images or visuals you don’t totally get. And all of those are un-disturbing, they are only there when you need them.</li> <li><strong>Search Everything</strong>: Type a few letters and find what you’re looking for instantly, even if you copied it days ago.</li></ul> <h2>Why I Chose Mac Only</h2> <p>Mac is my OS of choice, and I know well what Mac users expect. They care about design and simplicity. They want apps that don’t get in the way. Instead of trying to build for everyone, I focused on building something that feels natural on macOS.</p> <h2>The Technical Side</h2> <p>Building ClipBolt taught me a lot about macOS development. The hardest part was making it fast. When you press the keyboard shortcut, the app needs to appear instantly. No waiting, no loading screens.</p> <p>One thing I needed to figure out is handling different types of content. Text is easy, but images, and formatted content needed more effort.</p> <h2>The feedback I got</h2> <p>The early feedback has been encouraging. They like that it’s simplicity and clean interface. One said they like how it doesn’t feel overwhelming but have everything they needed. Other liked the extra features backed-in.</p> <p>A few developers mentioned they love how it handles code snippets. It keeps formatting and makes it easy to find old code they copied.</p> <h2>Where It’s Going</h2> <p>Right now, I’m working on publishing ClipBolt so it enters the Beta phase. I’m working with early users to fix bugs and add features they actually want.</p> <p>If you’re interested, you can join the <a href="https://tryclipbolt.com/" rel="noopener" target="_blank">waitlist</a> to get notified when it’s ready. I would strongly encourage joining the waitlist because I will know someone else care about it. Early subscribers will get lifetime free access to ClipBolt, even when it turns into a paid product.</p> <p>Sometimes the best products come from solving your own problems. That’s what ClipBolt is for me.</p> <p>Thanks for reading <span class="inline-flex translate-y-px"><!----><svg viewBox="0 0 36 36" width="1em" height="1em" ><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18c-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"/><ellipse cx="11.5" cy="16.5" fill="#664500" rx="2.5" ry="3.5"/><path fill="#664500" d="M28.457 17.797c-.06-.135-1.499-3.297-4.457-3.297s-4.397 3.162-4.457 3.297a.503.503 0 0 0 .755.605c.012-.009 1.262-.902 3.702-.902c2.426 0 3.674.881 3.702.901a.498.498 0 0 0 .755-.604M5.999 12.458a1 1 0 0 1-.799-1.6c3.262-4.35 7.616-4.4 7.8-4.4a1 1 0 0 1 .004 2c-.156.002-3.569.086-6.205 3.6a1 1 0 0 1-.8.4m23.002 2.125a1 1 0 0 1-.801-.4c-2.592-3.457-6.961-2.627-7.004-2.62a1 1 0 0 1-.393-1.961c.231-.047 5.657-1.072 8.996 3.38a1 1 0 0 1-.798 1.601m-5.747 8.994a.51.51 0 0 0-.597.06c-.01.008-1.013.863-4.657.863c-3.641 0-4.646-.854-4.646-.854a.5.5 0 0 0-.838.475c.01.044 1.144 4.379 5.484 4.379s5.474-4.335 5.485-4.379a.5.5 0 0 0-.231-.544"/></svg><!----></span><!----></p><!--]-->]]></content>
      </entry>
    <entry>
        <title>mastering-sveltekit-deployment-5</title>
        <link href="https://fayez.io/blog/mastering-sveltekit-deployment-5/"/>
        <id>https://fayez.io/blog/mastering-sveltekit-deployment-5/</id>
        <updated>2024-01-15T08:16:00.000Z</updated>
        <published>2024-01-15T08:16:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>In this part of the series, we will deploy a static version of our SvelteKit app to AWS S3 and serve it via AWS CloudFront. We will use <code>SST</code> for this purpose, which is a tool that significantly simplifies the deployment process for us. Let’s get started <span class="inline-flex translate-y-px"><!----><svg viewBox="0 0 36 36" width="1em" height="1em" ><path fill="#F4900C" d="M35 19a17 17 0 0 0-1.04-5.868c-.46 5.389-3.333 8.157-6.335 6.868c-2.812-1.208-.917-5.917-.777-8.164c.236-3.809-.012-8.169-6.931-11.794c2.875 5.5.333 8.917-2.333 9.125c-2.958.231-5.667-2.542-4.667-7.042c-3.238 2.386-3.332 6.402-2.333 9c1.042 2.708-.042 4.958-2.583 5.208c-2.84.28-4.418-3.041-2.963-8.333A16.94 16.94 0 0 0 1 19c0 9.389 7.611 17 17 17s17-7.611 17-17"/><path fill="#FFCC4D" d="M28.394 23.999c.148 3.084-2.561 4.293-4.019 3.709c-2.106-.843-1.541-2.291-2.083-5.291s-2.625-5.083-5.708-6c2.25 6.333-1.247 8.667-3.08 9.084c-1.872.426-3.753-.001-3.968-4.007A11.96 11.96 0 0 0 6 30c0 .368.023.73.055 1.09C9.125 34.124 13.342 36 18 36s8.875-1.876 11.945-4.91c.032-.36.055-.722.055-1.09c0-2.187-.584-4.236-1.606-6.001"/></svg><!----></span><!----></p> <h2>What is SST</h2> <p>SST is a tool built around AWS services that aims to simplify the process of building and deploying web applications. This tool has been receiving a lot of attention lately, as developers have always been looking for ways to deploy their apps to AWS quickly and avoid the hassle of working with the AWS console.</p> <p>One great thing about SST is the wide compatibility it offers, allowing to deploy a variety of services including APIs, serverless apps, and even static websites.</p> <blockquote><p>Before proceeding, make sure you have <code>aws-cli</code> installed and that you have logged in to your AWS account with a region of your choice, follow <a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html" rel="noopener" target="_blank">This guide on the AWS docs</a> to set it up.</p></blockquote> <h2>Using SST to deploy our static SvelteKit app</h2> <p>Let’s begin by checking out the <code>static</code> branch of <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" rel="noopener" target="_blank">our example app</a>, that we built in <a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" rel="noopener" target="_blank">Part 2 of the series</a>.</p> <pre class="language-undefined"><!----><code class="language-undefined">cd deployed-sveltekit-app
git checkout static</code><!----></pre> <p>Make sure you have your npm dependencies installed:</p> <pre class="language-zsh"><!----><code class="language-zsh">pnpm install</code><!----></pre> <p>And let’s build the project to generate a <code>build</code> directory, which we will deploy later on:</p> <pre class="language-zsh"><!----><code class="language-zsh">pnpm build</code><!----></pre> <p>Now create an SST project within the app root directory:</p> <pre class="language-undefined"><!----><code class="language-undefined">pnpx create-sst@latest</code><!----></pre> <p>When you run this command, it will prompt you with the following:</p> <pre class="language-undefined"><!----><code class="language-undefined">You are in a Svelte project so SST will be setup in drop-in mode. Continue? (Y/n):</code><!----></pre> <p>This will setup an SST project within our SvelteKit app, agree by pressing the key <code>Y</code>.</p> <p>Once you execute the command, you will notice that it makes some changes to your project files. Let’s examine the file <code>sst.config.ts</code> in the root directory:</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">&#123;</span> SSTConfig <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'sst'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> SvelteKitSite <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'sst/constructs'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">&#123;</span>
	<span class="token function">config</span><span class="token punctuation">(</span>_input<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
			name<span class="token operator">:</span> <span class="token string">'deployed-sveltekit-app'</span><span class="token punctuation">,</span>
			region<span class="token operator">:</span> <span class="token string">'us-east-1'</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
	<span class="token function">stacks</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		app<span class="token punctuation">.</span><span class="token function">stack</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token function">Site</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> stack <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			<span class="token keyword">const</span> site <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SvelteKitSite</span><span class="token punctuation">(</span>stack<span class="token punctuation">,</span> <span class="token string">'site'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			stack<span class="token punctuation">.</span><span class="token function">addOutputs</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
				url<span class="token operator">:</span> site<span class="token punctuation">.</span>url
			<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span> satisfies SSTConfig<span class="token punctuation">;</span></code><!----></pre> <p>This file contains the code which will instruct SST to deploy your app to AWS, and being Typesafe is a blessing for developers like us!</p> <p>The domain <code>ssr/constructs</code> has contains a set of functions that execute templates to be deployed using <a href="https://aws.amazon.com/cloudformation/" rel="noopener" target="_blank">AWS CloudFormation</a>. When we ran the command, SST automatically detected that we are on a SvelteKit project and it imported the <code>SvelteKitSite</code> construct, this function is too useful for deploying full-stack SvelteKit applications, but we are building a static website, so we have a few things that we need to change.</p> <p>Instead of using <code>SvelteKitSite</code>, let’s replace the import with <code>StaticSite</code> and specify the path to our built static site directory <code>./build</code>. I have also replaced the app region (The region doesn’t matter that much for these apps, the CloudFront and S3 distributions will not use an AWS region, only CloudFormation will):</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">&#123;</span> SSTConfig <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'sst'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> StaticSite <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'sst/constructs'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">&#123;</span>
	<span class="token function">config</span><span class="token punctuation">(</span>_input<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
			name<span class="token operator">:</span> <span class="token string">'deployed-sveltekit-app'</span><span class="token punctuation">,</span>
			region<span class="token operator">:</span> <span class="token string">'us-east-2'</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
	<span class="token function">stacks</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		app<span class="token punctuation">.</span><span class="token function">stack</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token function">Site</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> stack <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			<span class="token keyword">const</span> site <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StaticSite</span><span class="token punctuation">(</span>stack<span class="token punctuation">,</span> <span class="token string">'site'</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>
				path<span class="token operator">:</span> <span class="token string">'./build'</span>
			<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			stack<span class="token punctuation">.</span><span class="token function">addOutputs</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
				url<span class="token operator">:</span> site<span class="token punctuation">.</span>url
			<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span> satisfies SSTConfig<span class="token punctuation">;</span></code><!----></pre> <p>Another thing we need to do is to reset the changes made in <code>svelte.config.js</code>, at the time of writing this blog post, the changes made by SST do not work well with SvelteKit 2, also; we need the <code>adapter-static</code>, not the <code>sst</code> adapter:</p> <pre class="language-zsh"><!----><code class="language-zsh">git checkout svelte.config.js</code><!----></pre> <p>Before committing deployment, we need to run the dev command at least once to init our SSR project:</p> <pre class="language-zsh"><!----><code class="language-zsh">pnpm dev</code><!----></pre> <p>You will be prompted with this question to enter a name for your personal stage. A stage is an SST environment that allows you to dictate certain configs for dev, prod…etc. enter the name you would like to use and proceed.</p> <p>When the command completes running, it will then run your dev server. Let’s test it to see that everything works as expected:</p> <img alt="SST dev running the SvelteKit app" src="/blog/sst-dev-testing.webp"> <p>Great! Now that’s all what we have to do, kill the dev server or open a new terminal, and let’s deploy!</p> <pre class="language-zsh"><!----><code class="language-zsh">pnpm sst deploy --stage prod</code><!----></pre> <p>Note that we have specified the <code>prod</code> stage for the deployment environment. SST will automatically create an <a href="https://aws.amazon.com/s3/" rel="noopener" target="_blank">S3</a> bucket and a <a href="https://aws.amazon.com/cloudfront/" rel="noopener" target="_blank">CloudFront</a> distribution, and those will be part of a new <a href="https://aws.amazon.com/cloudformation/" rel="noopener" target="_blank">AWS CloudFormation</a> stack. It organizes your project greatly that allows you to change or remove it with ease.</p> <p>After the command finishes, you can find your app URL displayed in the console, resembling <code>https://&lt;id>.cloudfront.net</code>. Head to that link and you will see your app deployed and working as you would like to!</p> <img alt="CloudFormation stacks automatically created by SST" src="/blog/deployed-sveltekit-app-sst.gif"> <p>If you head to your AWS console, you see SST has created some new stacks for us, what’s relevant here is the <code>prod-deployed-sveltekit-app-Site</code> which contains the S3 bucket and CloudFront distribution for our app.</p> <img alt="CloudFormation stacks automatically created by SST" src="/blog/sst-cloudformation.webp"> <blockquote><p>We made the following changes here in the <code>static-s3</code> branch, which is based on the <code>static</code> branch.</p></blockquote> <h2>Adding custom domain to our SST App</h2> <p>With SST, we can set up a custom domain name for our app easily, whether it was inside or outside our AWS account.</p> <p>We can do this by modifying <code>sst.config.ts</code>, adding <code>customDomain</code> property to the <code>StaticSite</code> configuration.</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">&#123;</span> SSTConfig <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'sst'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> StaticSite <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'sst/constructs'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">&#123;</span>
	<span class="token function">config</span><span class="token punctuation">(</span>_input<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
			name<span class="token operator">:</span> <span class="token string">'deployed-sveltekit-app'</span><span class="token punctuation">,</span>
			region<span class="token operator">:</span> <span class="token string">'eu-west-1'</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
	<span class="token function">stacks</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		app<span class="token punctuation">.</span><span class="token function">stack</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token function">Site</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> stack <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			<span class="token keyword">const</span> site <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StaticSite</span><span class="token punctuation">(</span>stack<span class="token punctuation">,</span> <span class="token string">'site'</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>
				path<span class="token operator">:</span> <span class="token string">'./build'</span><span class="token punctuation">,</span>
				customDomain<span class="token operator">:</span> <span class="token string">'deployed-sveltekit-app.com'</span>
			<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			stack<span class="token punctuation">.</span><span class="token function">addOutputs</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
				url<span class="token operator">:</span> site<span class="token punctuation">.</span>url
			<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span> satisfies SSTConfig<span class="token punctuation">;</span></code><!----></pre> <p>If we pass this property alone, it will assume that we have created a new <a href="https://aws.amazon.com/route53/" rel="noopener" target="_blank">AWS Route53</a> hosted zone and purchased the domain name already.</p> <p>Now if we run the deployment command again, it will modify our app accordingly.</p> <pre class="language-zsh"><!----><code class="language-zsh">pnpm sst deploy --stage prod</code><!----></pre> <p>I’m not going to purchase that domain, so I’ll leave you here <span class="inline-flex translate-y-px"><!----><svg viewBox="0 0 36 36" width="1em" height="1em" ><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18c-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"/><path fill="#66471B" d="M15.457 15.815c-.06-.135-1.499-3.297-4.457-3.297s-4.397 3.162-4.457 3.297a.499.499 0 0 0 .754.605c.013-.009 1.262-.902 3.703-.902c2.426 0 3.674.881 3.702.901a.496.496 0 0 0 .609-.01a.5.5 0 0 0 .146-.594"/><path fill="#F5F8FA" d="M31 13.5a6.5 6.5 0 1 1-13 0a6.5 6.5 0 0 1 13 0"/><circle cx="24.5" cy="13.5" r="2.5" fill="#292F33"/><path fill="#66471B" d="M7 21.262c0 3.964 4.596 9 11 9s11-5 11-9c0 0-10.333 2.756-22 0"/><path fill="#E8596E" d="m18.545 23.604l-1.091-.005c-3.216-.074-5.454-.596-5.454-.596v6.961c0 3 2 6 6 6s6-3 6-6v-6.92a29 29 0 0 1-5.455.56"/><path fill="#DD2F45" d="M18 31.843a.545.545 0 0 0 .545-.545v-7.694l-1.091-.005v7.699a.546.546 0 0 0 .546.545"/></svg><!----></span><!---->.</p> <blockquote><p>For further details on specifying external domains, see <a href="https://docs.sst.dev/constructs/StaticSite#configuring-externally-hosted-domain" rel="noopener" target="_blank">SST StaticSite Documentation</a>.</p></blockquote> <p>That was a short post with great accomplishments! Thanks to SST for simplifying the process for us.</p> <h3 class="text-gray-400">Useful Links:</h3> <ul><li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-1" target="_blank">Mastering SvelteKit Deployment Part 1: Getting Started</a></li> <li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" target="_blank">Mastering SvelteKit Deployment Part 2: Adapters, What Are They?</a></li> <li><a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" target="_blank">Example App GitHub repo</a></li></ul><!--]-->]]></content>
      </entry>
    <entry>
        <title>mastering-sveltekit-deployment-4</title>
        <link href="https://fayez.io/blog/mastering-sveltekit-deployment-4/"/>
        <id>https://fayez.io/blog/mastering-sveltekit-deployment-4/</id>
        <updated>2023-12-17T06:16:00.000Z</updated>
        <published>2023-12-17T06:16:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>In <a href="https://fayez.io/blog/mastering-sveltekit-deployment-3" rel="noopener" target="_blank">part 3</a>, we deployed our SvelteKit app to <a href="https://vercel.com/" rel="noopener" target="_blank">Vercel</a>. We have gotten into details that were abstracted from us to gain more knowledge. In this part, we will explore the worldwide popular <a href="https://www.cloudflare.com/" rel="noopener" target="_blank">Cloudflare</a> platform. We will dive deep at a lower level, and it will be an enjoyable experience. Let’s get started!</p> <h2>What is Cloudflare</h2> <p>Cloudflare is an edge-powered distributed network that aims to serve secure and more performant web applications. It won the dignity in the web world as one of the biggest networks, serving millions of requests every second.</p> <p>One of the well-known services offered by this platform is <code>Cloudflare Workers</code>, A Serverless solution that allows for deploying web applications across the edge, bringing them closer to the user for a significant improvement in loading speeds and availability. Its successor - <code>Cloudflare Pages</code> offers substantial support for static and dynamic web applications by integrating with different Cloudflare services in an automated way as needed.</p> <p>In this blog post, we will deploy our app to <code>CloudFlare Pages</code> and empower it to offer ISR support which will allow us to experience the lower level <code>Cloudflare Workers</code> service. In the end we will deploy a pure static version of our website to <code>CloudFlare Pages</code>. Prepare your coffee, and bear with me <span class="inline-flex translate-y-px"><!----><svg viewBox="0 0 36 36" width="1em" height="1em" ><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18c-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"/><ellipse cx="11.5" cy="16.5" fill="#664500" rx="2.5" ry="3.5"/><path fill="#664500" d="M28.457 17.797c-.06-.135-1.499-3.297-4.457-3.297s-4.397 3.162-4.457 3.297a.503.503 0 0 0 .755.605c.012-.009 1.262-.902 3.702-.902c2.426 0 3.674.881 3.702.901a.498.498 0 0 0 .755-.604M5.999 12.458a1 1 0 0 1-.799-1.6c3.262-4.35 7.616-4.4 7.8-4.4a1 1 0 0 1 .004 2c-.156.002-3.569.086-6.205 3.6a1 1 0 0 1-.8.4m23.002 2.125a1 1 0 0 1-.801-.4c-2.592-3.457-6.961-2.627-7.004-2.62a1 1 0 0 1-.393-1.961c.231-.047 5.657-1.072 8.996 3.38a1 1 0 0 1-.798 1.601m-5.747 8.994a.51.51 0 0 0-.597.06c-.01.008-1.013.863-4.657.863c-3.641 0-4.646-.854-4.646-.854a.5.5 0 0 0-.838.475c.01.044 1.144 4.379 5.484 4.379s5.474-4.335 5.485-4.379a.5.5 0 0 0-.231-.544"/></svg><!----></span><!----></p> <h2>Cloudflare Edge Network</h2> <p>Cloudflare has hundreds of Data Centers on the world for serving web applications (services) deployed to their platform using the Serverless architecture, and by running every service on every data center, any user vising you website will receive a response from the closest location, which significantly reduces the latency and make your web app experience feel faster. We know this as the <a href="https://www.cloudflare.com/network" rel="noopener" target="_blank">Cloudflare Edge Network</a> and it’s one of the biggest reasons to deploy your web app to Cloudflare.</p> <h2>Cloudflare Workers</h2> <p>When you heard the word Serverless, your thought may be that it will be slow, that’s because we have always been told that the Serverless architecture spans up containers that take seconds to load on the first visit, known as “cold starts”. Cloudflare Workers solved this problem using their <a href="https://developers.cloudflare.com/workers/learning/how-workers-works/#isolates" rel="noopener" target="_blank">Isolates technology</a>. In short, Isolates are lightweight nodes running over the edge using V8 engine, being tiny and efficient minimizes the cold starts down to 5 milliseconds or fewer (It’s technically zero, see the notes below).</p> <blockquote><p>In <a href="https://fayez.io/blog/mastering-sveltekit-deployment-3" rel="noopener" target="_blank">Part 3</a> we have mentioned Vercel’s <a href="https://edge-runtime.vercel.app/" rel="noopener" target="_blank">Edge Runtime</a> feature, they are doing the same thing as Cloudflare Isolates technology but with different branding.</p></blockquote> <blockquote><p>Cloudflare has even gotten this to the next level by starting up the isolate as soon as the TLS handshake process starts its execution. By doing this, they were able to minimize the startup time down to 0 milliseconds! For more details, see <a href="https://blog.cloudflare.com/eliminating-cold-starts-with-cloudflare-workers" rel="noopener" target="_blank">this article on Cloudflare blog</a>.</p></blockquote> <h2>Cloudflare CDN</h2> <p>It’s likely that your website has static assets such as images, videos, markdown files…etc. For SvelteKit, you would often add your static assets to the <code>static</code> directory. Accessing those files from your server URL is easy to do. For example, we could access our favicon using <code>[APP_URL]/favicon.ico</code>.</p> <p>Those files are in your server, nowhere else. Now imagine a lot of users requesting those static assets from your server. This is a lot of traffic that can exhaust the server and increase latency. In addition, your server exists in one location, the more the distance between your user and the server, the greater the latency, thus your website will feel slower. Let’s say, for example, we have a server in Frankfurt and a user visiting it from Japan. We don’t want this huge trip to load our assets. What’s the solution?</p> <p>Use a Cloud Delivery Platform - CDN! It’s a layer between your server and the users. A CDN will distribute your static assets all over the globe, and some CDNs will also cache your assets for you. Cloudflare CDN is one of those services, which is identified as a CDN Cache - It performs content distribution &amp; Caching, minimizing loading time and speeding up your assets delivery.</p> <h2>Deploying to Cloudflare pages</h2> <p>Pages is the fastest way to deploy your web app to Cloudflare. It offers an automated Git integration that connects to your repo with auto-deployment and previews URL support. The great thing about this service is that it auto-configures everything you need for you on your behalf, you will get your app deployed to <a href="https://www.cloudflare.com/network/" rel="noopener" target="_blank">Cloudflare’s Edge Network</a>, it will also cache and distribute your static assets on <a href="https://www.cloudflare.com/application-services/products/cdn/" rel="noopener" target="_blank">Cloudflare CDN</a>.</p> <p>Pages support both static websites and dynamic apps, and the latter is done by an automated integration with Cloudflare Workers. This section will use <code>adapter-cloudflare</code>, which adapts your SvelteKit app to <a href="https://developers.cloudflare.com/pages/platform/functions/" rel="noopener" target="_blank">Cloudflare Functions API</a> . This adapter is suitable for the whole dynamic version of <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" rel="noopener" target="_blank">our example app</a>. We will cover the static version in a later part of this article.</p> <blockquote><p>If you have already forked the example app repo, checkout the <code>cloudflare</code> branch</p></blockquote> <p>Let’s begin by installing the adapter:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">pnpm</span> i  <span class="token parameter variable">-D</span>  @sveltejs/adapter-cloudflare</code><!----></pre> <p>Then update <code>svelte.config.js</code>:</p> <pre class="language-jsx"><!----><code class="language-jsx"><span class="token keyword">import</span> adapter <span class="token keyword">from</span> <span class="token string">'@sveltejs/adapter-cloudflare'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> vitePreprocess <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'@sveltejs/kit/vite'</span><span class="token punctuation">;</span>

<span class="token comment">/** @type &#123;import('@sveltejs/kit').Config&#125; */</span>

<span class="token keyword">const</span> config <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	<span class="token literal-property property">preprocess</span><span class="token operator">:</span> <span class="token function">vitePreprocess</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
	<span class="token literal-property property">kit</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
		<span class="token literal-property property">adapter</span><span class="token operator">:</span> <span class="token function">adapter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> config<span class="token punctuation">;</span></code><!----></pre> <p>Let’s have some fun and explore the build output of this adapter:</p> <pre class="language-tsx"><!----><code class="language-tsx">
pnpm build
</code><!----></pre> <p>Expand the content of the <code>.svelte-kit/cloudflare</code> directory. And you will notice a file tree similar to the following:</p> <pre class="language-bash"><!----><code class="language-bash">.svelte-kit/cloudflare

├── <span class="token number">404</span>.html
├── _app
│ ├──  immutable
│ │  ├──  assets
│ │  │  └──  *.css
│ │  ├──  chunks
│ │  │  └──  *.js
│ │  ├──  entry
│ │  │  └──  *.js
│ │  └──  nodes
│ │  └──  *.js
│ └──  version.json
├── _headers
├── _routes.json
├── _worker.js
├── _worker.js.map
├── favicon.png
├── ssg
│ └──  __data.json
└── ssg.html</code><!----></pre> <ul><li><p>The <code>_app/immutable</code> directory contains your compiled Vite output. Your browser will cache those for you because of the specified cache header by Vite. Don’t worry, the files are fingerprinted for you!</p></li> <li><p><code>assets</code> contain your static assets served by Vite. This differs from files inside the <code>static</code> directory (which SvelteKit doesn’t expose a <code>Cache-Control</code> header for).</p></li> <li><p><code>chunks</code> contain your compiled Svelte components separated into chunks.</p></li> <li><p><code>nodes</code> handle the server-side rendering of your app. It imports the files from the <code>chunks</code> directory to render in the server.</p></li> <li><p><code>entry</code> has the files that start up your web app, as the name shows, the entry point of your app, which imports files from <code>assets</code> and <code>nodes</code> and <code>chunks</code> to get your app running on both client and server-side.</p></li> <li><p>The <code>headers</code> file contains headers for the <code>_app</code> directory. It exposes a <code>Cache-Control</code> of <code>31536000 seconds</code> or <code>1 year</code> to be cached locally in the browser. Note that with Cloudflare, we have both browser and CDN caching. The CDN is distributed over Cloudflare’s edge network, so you get your app’s maximum network performance boost.</p></li> <li><p>The <code>_routes.json</code> and <code>_worker.*</code> are Cloudflare-specific files following the (Cloudflare functions routing API) which are all part of <a href="https://developers.cloudflare.com/workers/runtime-apis/" rel="noopener" target="_blank">Cloudflare workers API</a>, that’s right! With dynamic applications like our app, <a href="https://pages.cloudflare.com/" rel="noopener" target="_blank">Cloudflare Pages</a> will integrate with <a href="https://workers.cloudflare.com/" rel="noopener" target="_blank">Cloudflare Workers</a> offering SSR support.</p></li> <li><p>The rest of the files are our static assets and static data. We have explained those in <a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" rel="noopener" target="_blank">Part 2</a> of the series.</p></li></ul> <p>Now, let’s see that working in action and deploy our app to Cloudflare. Sign up to <a href="https://dash.cloudflare.com/sign-up" rel="noopener" target="_blank">Cloudflare</a>, verify your email, and head to the <a href="https://dash.cloudflare.com/" rel="noopener" target="_blank">Dashboard</a>. In the sidebar navigation, choose <code>Workers &amp; Pages</code>. click the <code>Pages</code> tab and click on <code>Connect to Git</code>, and choose our <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" rel="noopener" target="_blank">example app repo</a>. Once Cloudflare redirects you back, you need to enter the project name, select <code>cloudflare</code> branch,  select <code>SvelteKit</code> for the framework preset, and set the build command to <code>pnpm build</code>.</p> <p>We also need to add the <code>Environment Variables</code> for our app, copy them from the <code>.env</code> file in our example app repo, then finally click <code>Save and Deploy</code></p> <p><img src="/blog/cloudflare-pages-deploying.gif" alt="Deploying example app to Cloudflare Pages"></p> <p><img src="/blog/cloudflare-pages-deployed.gif" alt="Deploying example app to Cloudflare Pages"></p> <p>And we get our app deployed <span class="inline-flex translate-y-px"><!----><svg viewBox="0 0 36 36" width="1em" height="1em" ><circle cx="17" cy="19" r="17" fill="#FFCC4D"/><ellipse cx="17.999" cy="26" fill="#664500" rx="2" ry="2.5"/><path fill="#664500" d="M8.111 21.383a1 1 0 0 1-.845-1.533c.916-1.453 3.701-3.938 7.69-2.962a1 1 0 0 1-.476 1.942c-3.604-.882-5.502 2.056-5.521 2.086a1 1 0 0 1-.848.467m11.973-3.742a1 1 0 0 1-.634-1.774c3.176-2.604 6.762-1.562 8.215-.646a1 1 0 0 1-1.064 1.694c-.138-.084-3.052-1.823-5.884.499a1 1 0 0 1-.633.227"/><path fill="#E2A62D" d="M13.346 31.273a.75.75 0 0 1-.722-.954c.437-1.54.258-3.029-.49-4.086c-.497-.702-1.205-1.131-1.943-1.178c-.414-.025-.728-.382-.702-.795s.381-.751.795-.701c1.193.074 2.313.733 3.073 1.807c1.011 1.429 1.27 3.383.709 5.361a.75.75 0 0 1-.72.546m11.037-3.061a.74.74 0 0 1-.412-.124c-1.167-.77-1.82-2.117-1.792-3.695c.029-1.635.809-3.153 1.984-3.869a.75.75 0 0 1 1.03.251a.75.75 0 0 1-.251 1.03c-.735.448-1.244 1.499-1.264 2.614c-.02 1.055.389 1.936 1.118 2.417a.75.75 0 0 1-.413 1.376"/><path fill="#DD2E44" d="M17.179 2.72a.33.33 0 0 0-.189-.091S1.066-.394.377.214C-.311.823.74 16.998.74 16.998c.005.081.023.15.067.199c.604.684 4.758-2.004 9.279-6.001c4.522-3.998 7.697-7.792 7.093-8.476"/><path fill="#EA596E" d="M.349.271a.5.5 0 0 0-.038.123C.47 1.765 2.006 13.046 2.963 16.572c1.436-.803 2.895-1.894 4.609-3.253C6.116 10.654 1.158.146.349.271"/><path fill="#3B88C3" d="m29.902 29.229l-10.573-1.303c-1.13-.102-3.117-.112-3.015-1.902c.093-1.623 2.04-1.373 3.479-1.16l10.638 1.774z"/><path fill="#88C9F9" d="m30.43 26.639l-4.222-.724c-.494-.089-.934.647-.956 1.426c-.025.866.227 1.304.726 1.406l4.144.512z"/><path fill="#3B88C3" d="m34.918 26.341l-2.622 2.411l-4.687-5.097l2.622-2.411a3.37 3.37 0 0 1 4.751.199l.135.147a3.373 3.373 0 0 1-.199 4.751"/><ellipse cx="29.952" cy="26.203" fill="#88C9F9" rx="2.77" ry="3.462" transform="rotate(-42.597 29.954 26.205)"/><ellipse cx="29.952" cy="26.203" fill="#269" rx="1.385" ry="2.077" transform="rotate(-42.597 29.954 26.205)"/><circle cx="2.5" cy="33.5" r="1.5" fill="#55ACEE"/><circle cx="29" cy="2" r="2" fill="#55ACEE"/><path fill="#EA596E" d="M4.864 29.246L2.526 23.63L.412 29.675zM26 5l-4 1l1-4z"/><path fill="#77B255" d="M31.999 13L36 7.999L33 6z"/></svg><!----></span><!----></p> <p>We see our example web app is working well. The ISR page, however, wasn’t working as expected, it behaves completely as an SSR page. That’s because, unlike Vercel, the Cloudflare adapter does not offer ISR support out of the box, and the page specific <code>config</code> option will be ignored.</p> <h2>Adding ISR support to CloudFlare adapter</h2> <p>In <a href="https://fayez.io/blog/mastering-sveltekit-deployment-3" rel="noopener" target="_blank">Part 3</a> of the series, we explored the Incremental Static Generation and how well Vercel supports it. CloudFlare, however, doesn’t support this option out of the box, but it turned out that we could easily achieve it.</p> <h3>What is ISR?</h3> <p>ISR (or Incremental Static Regeneration) allows your deployed web page to be static with the ability to regenerate it in an interval of your choice.</p> <p>Let’s give an example with and without ISR:</p> <p>say you have a blog page that fetches your content from a headless CMS. You want your blog loaded as fast as possible, so deploying it as a static page is your best option for maximum performance.</p> <h3>Without ISR</h3> <p>You add the <code>export const prerender = true</code> to your page. This way, it will be generated at build time, but that’s the end. Even if you update your content, the <code>load</code> function will never rerun unless you redeploy your app, so your website will be outdated.</p> <h3>With ISR</h3> <p>With ISR, instead of pre-rendering your page at built time, you generate the static page on demand as soon as it’s visited. The first visit to your webpage will use SSR, and the result of the <code>load</code> function will be globally saved for a specified interval. Once the interval has expired, it will automatically regenerate on the next visit.</p> <blockquote><p>Don’t mislead ISR with browser Caching headers. Those will cache the page on the user’s browser for a specified amount of time upon the first visit. ISR caches them globally for all users for a specified period.</p></blockquote> <blockquote><p>Unlike pure static websites, ISR requires a server in order to work. So you must not prerender the web page where you intend to use ISR on.</p></blockquote> <h3>How to achieve ISR on Cloudflare</h3> <p>At the time of writing this post, there is no adapter that offers ISR support for SvelteKit, so we are going to write our own on the same project!</p> <p>We can use <a href="https://developers.cloudflare.com/kv/" rel="noopener" target="_blank">Cloudflare KV</a>. key value store to cache ISR pages on our example app. The great thing about KV is that you can store everything you need on it, including page content! And we can specify an expiration date for each key/value pair, which we will set to be the same as the ISR page expiration property.</p> <blockquote><p>You can see the result of our work in this section in the <code>cloudflare-isr</code> branch, which we are going to deploy at the end of the section.</p></blockquote> <p>Let’s begin by removing the cloudflare adapter:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">pnpm</span> remove @sveltejs/adapter-cloudflare</code><!----></pre> <p>Next, create a directory inside the root of the project. Let’s call it <code>cloudflare</code>. We are going to have 2 files in this directory:</p> <ul><li><p>index.js: Iterates through your SvelteKit’s routes and build an output file for each, respecting the configs passed to the adapter. In addition, this file outputs a <code>manifest</code> file containing deployment variables/configs for Cloudflare to read.</p></li> <li><p>files/worker.js: Cloudflare specific file for our deployed app, as the name hints, it follows <a href="https://developers.cloudflare.com/workers/runtime-apis/" rel="noopener" target="_blank">Cloudflare Workers Runtime APIs</a>, which is internally used by <a href="https://developers.cloudflare.com/pages/" rel="noopener" target="_blank">Cloudflare Pages</a> under the scenes.</p></li></ul> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">mkdir</span> cloudflare</code><!----></pre> <blockquote><p>Code in this section are originally taken from the official <a href="https://github.com/sveltejs/kit/blob/master/packages/adapter-cloudflare/index.js" rel="noopener" target="_blank">Cloudflare Adapter</a> sub-repo of SvelteKit GitHub repository.</p></blockquote> <blockquote></blockquote> <pre class="language-bash"><!----><code class="language-bash"><span class="token builtin class-name">cd</span> cloudflare
<span class="token function">touch</span> index.js
<span class="token function">mkdir</span> files
<span class="token function">touch</span> files/worker.js</code><!----></pre> <p>Paste this code on the file <code>index.js</code>:</p> <pre class="language-jsx"><!----><code class="language-jsx"><span class="token keyword">import</span> <span class="token punctuation">&#123;</span> writeFileSync <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'node:fs'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> path <span class="token keyword">from</span> <span class="token string">'node:path'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> fileURLToPath <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'node:url'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> esbuild <span class="token keyword">from</span> <span class="token string">'esbuild'</span><span class="token punctuation">;</span>

<span class="token comment">/** @type &#123;import('./index.js').default&#125; */</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">defaults <span class="token operator">=</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span></span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
	<span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
		<span class="token literal-property property">name</span><span class="token operator">:</span> <span class="token string">'@sveltejs/adapter-cloudflare'</span><span class="token punctuation">,</span>
		<span class="token keyword">async</span> <span class="token function">adapt</span><span class="token punctuation">(</span><span class="token parameter">builder</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			<span class="token keyword">const</span> files <span class="token operator">=</span> <span class="token function">fileURLToPath</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span><span class="token string">'./files'</span><span class="token punctuation">,</span> <span class="token keyword">import</span><span class="token punctuation">.</span>meta<span class="token punctuation">.</span>url<span class="token punctuation">)</span><span class="token punctuation">.</span>href<span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token keyword">const</span> dest <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">getBuildDirectory</span><span class="token punctuation">(</span><span class="token string">'cloudflare'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">const</span> tmp <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">getBuildDirectory</span><span class="token punctuation">(</span><span class="token string">'cloudflare-tmp'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			builder<span class="token punctuation">.</span><span class="token function">rimraf</span><span class="token punctuation">(</span>dest<span class="token punctuation">)</span><span class="token punctuation">;</span>
			builder<span class="token punctuation">.</span><span class="token function">rimraf</span><span class="token punctuation">(</span>tmp<span class="token punctuation">)</span><span class="token punctuation">;</span>
			builder<span class="token punctuation">.</span><span class="token function">mkdirp</span><span class="token punctuation">(</span>tmp<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// generate 404.html first which can then be overridden by prerendering, if the user defined such a page</span>
			<span class="token keyword">await</span> builder<span class="token punctuation">.</span><span class="token function">generateFallback</span><span class="token punctuation">(</span>path<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span>dest<span class="token punctuation">,</span> <span class="token string">'404.html'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">const</span> dest_dir <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>dest<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>builder<span class="token punctuation">.</span>config<span class="token punctuation">.</span>kit<span class="token punctuation">.</span>paths<span class="token punctuation">.</span>base<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">;</span>
			<span class="token keyword">const</span> written_files <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">writeClient</span><span class="token punctuation">(</span>dest_dir<span class="token punctuation">)</span><span class="token punctuation">;</span>
			builder<span class="token punctuation">.</span><span class="token function">writePrerendered</span><span class="token punctuation">(</span>dest_dir<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token keyword">const</span> relativePath <span class="token operator">=</span> path<span class="token punctuation">.</span>posix<span class="token punctuation">.</span><span class="token function">relative</span><span class="token punctuation">(</span>tmp<span class="token punctuation">,</span> builder<span class="token punctuation">.</span><span class="token function">getServerDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token keyword">const</span> isr <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
			<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> route <span class="token keyword">of</span> builder<span class="token punctuation">.</span>routes<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
				<span class="token keyword">const</span> config <span class="token operator">=</span> <span class="token punctuation">&#123;</span> <span class="token operator">...</span>defaults<span class="token punctuation">,</span> <span class="token operator">...</span>route<span class="token punctuation">.</span>config <span class="token punctuation">&#125;</span><span class="token punctuation">;</span> <span class="token comment">// Add pages with isr config to the isr array, to be exported later.</span>

				<span class="token keyword">if</span> <span class="token punctuation">(</span>config<span class="token punctuation">.</span>isr<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
					isr<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
						<span class="token literal-property property">pathname</span><span class="token operator">:</span> route<span class="token punctuation">.</span>id<span class="token punctuation">,</span>
						<span class="token literal-property property">expiration</span><span class="token operator">:</span> config<span class="token punctuation">.</span>isr<span class="token punctuation">.</span>expiration
					<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
				<span class="token punctuation">&#125;</span>
			<span class="token punctuation">&#125;</span>

			<span class="token function">writeFileSync</span><span class="token punctuation">(</span>
				<span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>tmp<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/manifest.js</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">,</span>
				<span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">export const manifest = </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>builder<span class="token punctuation">.</span><span class="token function">generateManifest</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> relativePath <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">;&#92;n&#92;n</span><span class="token template-punctuation string">&#96;</span></span> <span class="token operator">+</span>
					<span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">export const prerendered = new Set(</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>builder<span class="token punctuation">.</span>prerendered<span class="token punctuation">.</span>paths<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">);&#92;n</span><span class="token template-punctuation string">&#96;</span></span> <span class="token operator">+</span>
					<span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">export const isr = </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span><span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>isr<span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">;&#92;n</span><span class="token template-punctuation string">&#96;</span></span>
			<span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token function">writeFileSync</span><span class="token punctuation">(</span>
				<span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>dest<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/_routes.json</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">,</span>
				<span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span><span class="token function">get_routes_json</span><span class="token punctuation">(</span>builder<span class="token punctuation">,</span> written_files<span class="token punctuation">,</span> defaults<span class="token punctuation">.</span>routes <span class="token operator">??</span> <span class="token punctuation">&#123;</span><span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">'&#92;t'</span><span class="token punctuation">)</span>
			<span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token function">writeFileSync</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>dest<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/_headers</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">,</span> <span class="token function">generate_headers</span><span class="token punctuation">(</span>builder<span class="token punctuation">.</span><span class="token function">getAppPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span> <span class="token literal-property property">flag</span><span class="token operator">:</span> <span class="token string">'a'</span> <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			builder<span class="token punctuation">.</span><span class="token function">copy</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>files<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/worker.js</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>tmp<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/_worker.js</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>
				<span class="token literal-property property">replace</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
					<span class="token constant">SERVER</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>relativePath<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/index.js</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">,</span>
					<span class="token constant">MANIFEST</span><span class="token operator">:</span> <span class="token string">'./manifest.js'</span>
				<span class="token punctuation">&#125;</span>
			<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token keyword">await</span> esbuild<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
				<span class="token literal-property property">platform</span><span class="token operator">:</span> <span class="token string">'browser'</span><span class="token punctuation">,</span>
				<span class="token literal-property property">conditions</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'worker'</span><span class="token punctuation">,</span> <span class="token string">'browser'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
				<span class="token literal-property property">sourcemap</span><span class="token operator">:</span> <span class="token string">'linked'</span><span class="token punctuation">,</span>
				<span class="token literal-property property">target</span><span class="token operator">:</span> <span class="token string">'es2022'</span><span class="token punctuation">,</span>
				<span class="token literal-property property">entryPoints</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>tmp<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/_worker.js</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">]</span><span class="token punctuation">,</span>
				<span class="token literal-property property">outfile</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>dest<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/_worker.js</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">,</span>
				<span class="token literal-property property">allowOverwrite</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
				<span class="token literal-property property">format</span><span class="token operator">:</span> <span class="token string">'esm'</span><span class="token punctuation">,</span>
				<span class="token literal-property property">bundle</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
				<span class="token literal-property property">loader</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
					<span class="token string-property property">'.wasm'</span><span class="token operator">:</span> <span class="token string">'copy'</span>
				<span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
				<span class="token literal-property property">external</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'cloudflare:*'</span><span class="token punctuation">]</span>
			<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/**
 * @param &#123;import('@sveltejs/kit').Builder&#125; builder
 * @param &#123;string[]&#125; assets
 * @param &#123;import('./index.js').AdapterOptions['routes']&#125; routes
 * @returns &#123;import('./index.js').RoutesJSONSpec&#125;
 */</span>

<span class="token keyword">function</span> <span class="token function">get_routes_json</span><span class="token punctuation">(</span>builder<span class="token punctuation">,</span> assets<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span> include <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'/*'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> exclude <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'&lt;all>'</span><span class="token punctuation">]</span> <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
	<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>include<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token operator">!</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>exclude<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'routes.include and routes.exclude must be arrays'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>

	<span class="token keyword">if</span> <span class="token punctuation">(</span>include<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'routes.include must contain at least one route'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>

	<span class="token keyword">if</span> <span class="token punctuation">(</span>include<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'routes.include must contain 100 or fewer routes'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>

	exclude <span class="token operator">=</span> exclude

		<span class="token punctuation">.</span><span class="token function">flatMap</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">rule</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">(</span>rule <span class="token operator">===</span> <span class="token string">'&lt;all>'</span> <span class="token operator">?</span> <span class="token punctuation">[</span><span class="token string">'&lt;build>'</span><span class="token punctuation">,</span> <span class="token string">'&lt;files>'</span><span class="token punctuation">,</span> <span class="token string">'&lt;prerendered>'</span><span class="token punctuation">]</span> <span class="token operator">:</span> rule<span class="token punctuation">)</span><span class="token punctuation">)</span>

		<span class="token punctuation">.</span><span class="token function">flatMap</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">rule</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
			<span class="token keyword">if</span> <span class="token punctuation">(</span>rule <span class="token operator">===</span> <span class="token string">'&lt;build>'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
				<span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>builder<span class="token punctuation">.</span><span class="token function">getAppPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/*</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">;</span>
			<span class="token punctuation">&#125;</span>

			<span class="token keyword">if</span> <span class="token punctuation">(</span>rule <span class="token operator">===</span> <span class="token string">'&lt;files>'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
				<span class="token keyword">return</span> assets
					<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>
						<span class="token punctuation">(</span><span class="token parameter">file</span><span class="token punctuation">)</span> <span class="token operator">=></span>
							<span class="token operator">!</span><span class="token punctuation">(</span>
								file<span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>builder<span class="token punctuation">.</span>config<span class="token punctuation">.</span>kit<span class="token punctuation">.</span>appDir<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">)</span> <span class="token operator">||</span>
								file <span class="token operator">===</span> <span class="token string">'_headers'</span> <span class="token operator">||</span>
								file <span class="token operator">===</span> <span class="token string">'_redirects'</span>
							<span class="token punctuation">)</span>
					<span class="token punctuation">)</span>
					<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">file</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>file<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token punctuation">&#125;</span>

			<span class="token keyword">if</span> <span class="token punctuation">(</span>rule <span class="token operator">===</span> <span class="token string">'&lt;prerendered>'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
				<span class="token keyword">const</span> prerendered <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

				<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> path <span class="token keyword">of</span> builder<span class="token punctuation">.</span>prerendered<span class="token punctuation">.</span>paths<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
					<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>builder<span class="token punctuation">.</span>prerendered<span class="token punctuation">.</span>redirects<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
						prerendered<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>path<span class="token punctuation">)</span><span class="token punctuation">;</span>
					<span class="token punctuation">&#125;</span>
				<span class="token punctuation">&#125;</span>

				<span class="token keyword">return</span> prerendered<span class="token punctuation">;</span>
			<span class="token punctuation">&#125;</span>

			<span class="token keyword">return</span> rule<span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

	<span class="token keyword">const</span> excess <span class="token operator">=</span> include<span class="token punctuation">.</span>length <span class="token operator">+</span> exclude<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">100</span><span class="token punctuation">;</span>

	<span class="token keyword">if</span> <span class="token punctuation">(</span>excess <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">const</span> message <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">Function includes/excludes exceeds _routes.json limits (see https://developers.cloudflare.com/pages/platform/functions/routing/#limits). Dropping </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>excess<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string"> exclude rules — this will cause unnecessary function invocations.</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">;</span>

		builder<span class="token punctuation">.</span>log<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
		exclude<span class="token punctuation">.</span>length <span class="token operator">-=</span> excess<span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>

	<span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
		<span class="token literal-property property">version</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
		<span class="token literal-property property">description</span><span class="token operator">:</span> <span class="token string">'Generated by @sveltejs/adapter-cloudflare'</span><span class="token punctuation">,</span>
		include<span class="token punctuation">,</span>
		exclude
	<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>

<span class="token comment">/** @param &#123;string&#125; app_dir */</span>

<span class="token keyword">function</span> <span class="token function">generate_headers</span><span class="token punctuation">(</span><span class="token parameter">app_dir</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
	<span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">

# === START AUTOGENERATED SVELTE IMMUTABLE HEADERS ===
/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>app_dir<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/*
  X-Robots-Tag: noindex
 Cache-Control: no-cache
/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>app_dir<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/immutable/*
  ! Cache-Control
 Cache-Control: public, immutable, max-age=31536000
# === END AUTOGENERATED SVELTE IMMUTABLE HEADERS ===
</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">.</span><span class="token function">trimEnd</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span></code><!----></pre> <p>This file builds and does the initial setup for our app using <code>esbuild</code> (which is used by vite internally). In this snippet, we also iterate through the app routes and save each isr page in the <code>isr</code> array, along with it’s expiration value. We export those values we obtained to the <code>manifest</code> file, which can be then imported using the <code>MANIFEST</code> alias.</p> <p>The cloudflare specific work is done within <code>files/workers.js</code>, open that file in your code editor and paste this content:</p> <pre class="language-jsx"><!----><code class="language-jsx"><span class="token keyword">import</span> <span class="token punctuation">&#123;</span> Server <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'SERVER'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> manifest<span class="token punctuation">,</span> prerendered<span class="token punctuation">,</span> isr <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'MANIFEST'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> Cache <span class="token keyword">from</span> <span class="token string">'worktop/cfw.cache'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> server <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Server</span><span class="token punctuation">(</span>manifest<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> worker <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	<span class="token keyword">async</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token parameter">req<span class="token punctuation">,</span> env<span class="token punctuation">,</span> context</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">await</span> server<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span> env <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Initialize server with the environment</span>
		<span class="token comment">// Check cache unless "no-cache" is requested</span>

		<span class="token keyword">if</span> <span class="token punctuation">(</span>req<span class="token punctuation">.</span>headers<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'cache-control'</span><span class="token punctuation">)</span> <span class="token operator">!==</span> <span class="token string">'no-cache'</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			<span class="token keyword">const</span> res <span class="token operator">=</span> <span class="token keyword">await</span> Cache<span class="token punctuation">.</span><span class="token function">lookup</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token keyword">if</span> <span class="token punctuation">(</span>res<span class="token punctuation">)</span> <span class="token keyword">return</span> res<span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span> <span class="token comment">// Get pathname from the request URL</span>

		<span class="token keyword">const</span> <span class="token punctuation">&#123;</span> <span class="token literal-property property">pathname</span><span class="token operator">:</span> rawPathname <span class="token punctuation">&#125;</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span>req<span class="token punctuation">.</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span>

		<span class="token keyword">const</span> pathname <span class="token operator">=</span> <span class="token function">decodeURIComponent</span><span class="token punctuation">(</span>rawPathname<span class="token punctuation">)</span> <span class="token operator">||</span> rawPathname<span class="token punctuation">;</span>

		<span class="token keyword">const</span> key <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">key_</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>pathname<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">;</span> <span class="token comment">// Attempt to serve from KV cache</span>

		<span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
			<span class="token keyword">const</span> cachedResponse <span class="token operator">=</span> <span class="token keyword">await</span> env<span class="token punctuation">.</span>kv<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token keyword">if</span> <span class="token punctuation">(</span>cachedResponse<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
				<span class="token keyword">const</span> cachedHeaders <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Headers</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
					<span class="token string-property property">'Content-Type'</span><span class="token operator">:</span> <span class="token string">'text/html'</span><span class="token punctuation">,</span>

					<span class="token string-property property">'CF-Cache-Status'</span><span class="token operator">:</span> <span class="token string">'HIT'</span>
				<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

				<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Response</span><span class="token punctuation">(</span>cachedResponse<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span> <span class="token literal-property property">headers</span><span class="token operator">:</span> cachedHeaders <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token punctuation">&#125;</span>
		<span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			<span class="token comment">// Skip if page is not in cache (we'll render and return the response below)</span>
		<span class="token punctuation">&#125;</span> <span class="token comment">// Determine if the request is for a static asset or prerendered page</span>

		<span class="token keyword">const</span> strippedPathname <span class="token operator">=</span> pathname<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">/$</span><span class="token regex-delimiter">/</span></span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

		<span class="token keyword">const</span> isStaticAsset <span class="token operator">=</span>
			manifest<span class="token punctuation">.</span>assets<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>strippedPathname<span class="token punctuation">)</span> <span class="token operator">||</span>
			manifest<span class="token punctuation">.</span>assets<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>strippedPathname<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/index.html</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>

		<span class="token keyword">const</span> location <span class="token operator">=</span> pathname<span class="token punctuation">.</span><span class="token function">endsWith</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span> <span class="token operator">?</span> strippedPathname <span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>pathname<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">;</span> <span class="token comment">// Serve assets, prerendered pages, or process dynamic requests</span>

		<span class="token keyword">let</span> res<span class="token punctuation">;</span>

		<span class="token keyword">if</span> <span class="token punctuation">(</span>isStaticAsset <span class="token operator">||</span> prerendered<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>pathname<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			res <span class="token operator">=</span> <span class="token keyword">await</span> env<span class="token punctuation">.</span><span class="token constant">ASSETS</span><span class="token punctuation">.</span><span class="token function">fetch</span><span class="token punctuation">(</span>req<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>location <span class="token operator">&amp;&amp;</span> prerendered<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>location<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			res <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Response</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span> <span class="token literal-property property">status</span><span class="token operator">:</span> <span class="token number">308</span><span class="token punctuation">,</span> <span class="token literal-property property">headers</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> location <span class="token punctuation">&#125;</span> <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span>
			res <span class="token operator">=</span> <span class="token keyword">await</span> server<span class="token punctuation">.</span><span class="token function">respond</span><span class="token punctuation">(</span>req<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>
				<span class="token literal-property property">platform</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span> env<span class="token punctuation">,</span> context<span class="token punctuation">,</span> caches<span class="token punctuation">,</span> <span class="token literal-property property">cf</span><span class="token operator">:</span> req<span class="token punctuation">.</span>cf <span class="token punctuation">&#125;</span><span class="token punctuation">,</span>

				<span class="token function-variable function">getClientAddress</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> req<span class="token punctuation">.</span>headers<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'cf-connecting-ip'</span><span class="token punctuation">)</span>
			<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// If it's an isr page, Store in KV cache if the response is successful</span>

			<span class="token keyword">const</span> isrPage <span class="token operator">=</span> isr<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">page</span><span class="token punctuation">)</span> <span class="token operator">=></span> page<span class="token punctuation">.</span>pathname <span class="token operator">===</span> pathname <span class="token operator">||</span> page<span class="token punctuation">.</span>pathname <span class="token operator">===</span> location<span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token keyword">if</span> <span class="token punctuation">(</span>isrPage <span class="token operator">&amp;&amp;</span> res<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
				<span class="token keyword">const</span> bodyText <span class="token operator">=</span> <span class="token keyword">await</span> res<span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

				<span class="token keyword">await</span> env<span class="token punctuation">.</span>kv<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> bodyText<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span> <span class="token literal-property property">expirationTtl</span><span class="token operator">:</span> isrPage<span class="token punctuation">.</span>expiration <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token punctuation">&#125;</span>
		<span class="token punctuation">&#125;</span> <span class="token comment">// Utilize Cache API for eligible responses</span>

		<span class="token keyword">if</span> <span class="token punctuation">(</span>res<span class="token punctuation">.</span>status <span class="token operator">&lt;</span> <span class="token number">400</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			<span class="token keyword">const</span> cacheControl <span class="token operator">=</span> res<span class="token punctuation">.</span>headers<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'cache-control'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

			<span class="token keyword">if</span> <span class="token punctuation">(</span>cacheControl<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
				<span class="token keyword">return</span> Cache<span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>req<span class="token punctuation">,</span> res<span class="token punctuation">,</span> context<span class="token punctuation">)</span><span class="token punctuation">;</span>
			<span class="token punctuation">&#125;</span>
		<span class="token punctuation">&#125;</span>

		<span class="token keyword">return</span> res<span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> worker<span class="token punctuation">;</span></code><!----></pre> <p>As explained, we store each ISR page inside Workers KV store and set the relevant <code>expirationTtl</code> value. You can access the Workers KV store using the <code>env.kv</code> variable.</p> <p>In the Cloudflare interface, we are going to use our previously deployed app switching the branch to <code>cloudflare-isr</code>. To set the branch, go to the project page > Settings > Builds &amp; Deployments ></p> <p>Configure Production Deployments.</p> <p>You need a KV name-space bound to your project environment, to create one, go to Workers &amp; Pages, KV, and create your name-space, then head to your project page > Settings > Functions > KV name-space bindings, and select the name-space for both Production &amp; Preview deployments, last, redeploy your app for the changes to take effect.</p> <p><img src="/blog/cloudflare-pages-isr-kv.gif" alt="Setting up KV for ISR"></p> <p>And finally, we got our ISR page working!</p> <blockquote><p>A downside of this approach is that our page will still behave like an SSR page in case of SPA, you can work around that by using a hard link for ISR page or disabling csr, though, I don’t see any need of that, ISR pages are usually done to increase speed and minimize traffic, but if your user have already loaded your website that won’t be needed most times and it won’t make that increased traffic you would worry about.</p></blockquote> <h2>Deploy as a pure static website</h2> <p>Cloudflare Pages also offer support for a fully static website. If your website is purely static, you can use <code>adapter-static</code> and get your website deployed with no backend code execution. Note that in this case your website isn’t any different from any static asset, so it will automatically get distributed to <code>Cloudflare CDN</code> while being cached to your users.</p> <p>Start by checking out the <code>static</code> branch, installing node dependencies and executing the build script:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">git</span> checkout static
<span class="token function">pnpm</span> <span class="token function">install</span>
<span class="token function">pnpm</span> build</code><!----></pre> <p><code>adapter-static</code> will generate a <code>build</code> directory generated for you, we explained the file architecture for this directory in <a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" rel="noopener" target="_blank">Part 2</a> of the series.</p> <p>Now all you have to do is push this code and deploy it using <code>Cloudflare pages</code> like we did in the previous part, except that we are going explicitly set the output directory to <code>build</code> and we will get our static website deployed <span class="inline-flex translate-y-px"><!----><svg viewBox="0 0 36 36" width="1em" height="1em" ><circle cx="18" cy="18" r="18" fill="#FFCC4D"/><path fill="#664500" d="M10.515 23.621C10.56 23.8 11.683 28 18 28s7.44-4.2 7.485-4.379a.5.5 0 0 0-.237-.554a.505.505 0 0 0-.6.077C24.629 23.163 22.694 25 18 25s-6.63-1.837-6.648-1.855a.5.5 0 0 0-.598-.081a.5.5 0 0 0-.239.557"/><ellipse cx="12" cy="13.5" fill="#664500" rx="2.5" ry="3.5"/><ellipse cx="24" cy="13.5" fill="#664500" rx="2.5" ry="3.5"/></svg><!----></span><!----></p> <p><img src="/blog/cloudflare-pages-static-deploying.gif" alt="Deploying Static website to Cloudflare Pages"></p> <p><img src="/blog/cloudflare-pages-static-deployed.gif" alt="Deployed Static website on Cloudflare Pages"></p> <h2>In closing</h2> <p>It was a long blog post. I hope you have enjoyed reading it as much as I’ve been enjoying writing this, in the next part we are going to deploy a pure static version of our website to AWS S3. See you then!</p> <h3 class="text-gray-400">Useful Links:</h3> <ul><li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-1" target="_blank">Mastering SvelteKit Deployment Part 1: Getting Started</a></li> <li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" target="_blank">Mastering SvelteKit Deployment Part 2: Adapters, What Are They?</a></li> <li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-3" target="_blank">Mastering SvelteKit Deployment Part 3: Deploying to Vercel</a></li> <li><a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" target="_blank">Example App GitHub repo</a></li></ul><!--]-->]]></content>
      </entry>
    <entry>
        <title>mastering-sveltekit-deployment-3</title>
        <link href="https://fayez.io/blog/mastering-sveltekit-deployment-3/"/>
        <id>https://fayez.io/blog/mastering-sveltekit-deployment-3/</id>
        <updated>2023-10-29T03:19:00.000Z</updated>
        <published>2023-10-29T03:19:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>In <a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" rel="noopener" target="_blank">part 2</a> of the series, we demonstrated the concept of SvelteKit’s Adapters and experimented with <code>adapter-node</code> annd <code>adapter-static</code>.</p> <p>From this part onwards, we will begin deploying our app to different platforms, with each step we will explain how the target platform works and what inputs it expects to get our app deployed.</p> <p>For this blog post, our target deployment platform will be <a href="https://vercel.com/" rel="noopener" target="_blank">Vercel</a>, known for its simplicity, customizability, and top-notch DevEx. We will start by explaining their deployment pipeline, serverless nature, supported runtimes, and static apps. Then, we will dive into using <code>adapter-vercel</code> and explain how it works behind the scenes. Finally, we will explore how to deploy our example app differently. Although this blog post may be longer than usual, I will try to simplify it as much as possible <span class="inline-flex translate-y-px"><!----><svg viewBox="0 0 36 36" width="1em" height="1em" ><circle cx="18" cy="18" r="18" fill="#FFCC4D"/><path fill="#664500" d="M10.515 23.621C10.56 23.8 11.683 28 18 28s7.44-4.2 7.485-4.379a.5.5 0 0 0-.237-.554a.505.505 0 0 0-.6.077C24.629 23.163 22.694 25 18 25s-6.63-1.837-6.648-1.855a.5.5 0 0 0-.598-.081a.5.5 0 0 0-.239.557"/><ellipse cx="12" cy="13.5" fill="#664500" rx="2.5" ry="3.5"/><ellipse cx="24" cy="13.5" fill="#664500" rx="2.5" ry="3.5"/></svg><!----></span><!----></p> <h2>What Is Vercel?</h2> <p>Vercel is a cloud-based platform that lets developers build and deploy their apps. They are known as the “Frontend Cloud”, meaning that, while they are a cloud-based product, they aim to simplify the process of building and shipping web applications for frontend developers, including a variety of features like hosting, storage, databases, caching, analytics, AI, and more.</p> <p>They are also known as part of the open-source community. They own and fund frameworks like Next.js and SvelteKit. However, SvelteKit offers broader platform support and compatibility and isn’t taught to Vercel.</p> <h2>How Vercel Works?</h2> <p>Vercel platform is based upon the serverless architecture, empowered by AWS Lambda functions with Amazon Linux 2 runtime. Being serverless means you cannot run long background tasks or <span class="”opacity-80”">HTTP streaming*</span> unless using an add-on like <a href="https://vercel.com/integrations/inngest" rel="noopener" target="_blank">inngest</a>.</p> <blockquote><p>*Although Vercel doesn’t natively support HTTP streaming, their team built a <a href="https://vercel.com/blog/streaming-for-serverless-node-js-and-edge-runtimes-with-vercel-functions" rel="noopener" target="_blank">custom solution</a> that makes this feature possible, which is done by triggering a bridged socket connection. It’s used by frameworks like Next.js and SvelteKit for their streaming support.</p></blockquote> <h2>Continuous Integration with Git</h2> <p>Vercel recommends deploying to their platform via Git for additional features like Continuous Integration, previewing deployments before merging to the main branch, and live feedback. In this part of the series, we will set up automatic deployment with <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app/tree/vercel" rel="noopener" target="_blank">vercel</a> and <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app/tree/vercel-edge" rel="noopener" target="_blank">vercel-edge</a> branches and set up a preview URL for each.</p> <h2>Vercel’s Supported Runtimes</h2> <p>When deploying your app on Vercel, it uses serverless functions. The runtime for these functions is supported in two different options.</p> <ul><li><p>Serverless Runtime: This is the default and usual runtime. It fully supports different runtimes like Node.js, Go, Python, Ruby, etc. However, it’s slower due to its cold starts and may even be expensive in some cases.</p></li> <li><p>Edge Runtime: With this option, your functions will run faster with minimal cold starts, as it runs with lightweight resources and uses Web API Standards. Edge functions are deployed through Vercel’s Edge Network, which allows significantly faster response times as content becomes closer to users.</p> <blockquote><p>It’s important to keep in mind that the Edge Runtime has some differences compared to Node.js. For example, it doesn’t support Node’s filesystem module.</p></blockquote></li></ul> <p>For a more detailed comparison, check out <a href="https://vercel.com/docs/infrastructure/runtime-comparison" rel="noopener" target="_blank">Vercel’s Runtime Comparison</a> article.</p> <h2>Content Caching</h2> <p>One thing that Vercel does so well is its caching system. Here’s an overview of its top features related to this topic:</p> <ul><li>For static files, Vercel will automatically cache them on their CDN using Vercel’s Edge Network. This will be automatically applied to files under <code>static</code> directory, as well as your Vite’s compiled fingerprinted static assets.</li> <li>Vercel was the first platform to add Incremental Static Regeneration support, which allows regenerating static<span class="”opacity-80”">/prerendered</span> pages based on time intervals.</li> <li>For pages with static and dynamic data, Vercel has a built-in granular cache known as <a href="https://vercel.com/docs/infrastructure/data-cache" rel="noopener" target="_blank">Vercel Data Cache</a>, though, at the time of writing, this feature is only available for Next.js (It does this by wrapping the global <code>fetch</code> function).</li></ul> <blockquote><p>Note that caching for fiels under <code>static</code> directory is done by only using CDN caching, as the time of writing this blog post, SvelteKit does not expost cache control headers for those files, you will only get a boost from serving those files over Vercel’s Edge Network, but the browsers won’t cache them for you.</p></blockquote> <h2>Page-Specific Adapter Configs</h2> <p>Before taking another look at our example app, let me mention that SvelteKit supports exporting page-specific configs, which are adapter-specific. For example, if we want to enable Incremental Static Regeneration for one of our static pages, in <code>page.server.ts</code> or <code>page.ts</code>, we can export a <code>config</code> object:</p> <pre class="language-tsx"><!----><code class="language-tsx"><span class="token keyword">export</span> config <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	isr<span class="token operator">:</span> <span class="token punctuation">&#123;</span>
		expiration<span class="token operator">:</span> <span class="token number">15</span> <span class="token comment">// regenerate every 15 seconds</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span></code><!----></pre> <p>This way, our static page will be rebuilt every 60 seconds. Note that page-specific configs are not supported for all adapters. It’s currently supported by <code>adapter-vercel</code>, which we will use in this part of the series.</p> <h2>A Closer Look at our Example App</h2> <p>To allow us to experiment with different features offered by multiple platforms, We have 5 pages with different rendering and caching strategies:</p> <ul><li>/ssr: Displays a list of comments. This page is server-side rendered and allows posting new comments, which triggers a form server-action.</li> <li>/ssr-streaming: Another server-rendered page, but the content here is streamed using SvelteKit’s streaming feature. Comments here are delayed for 2 seconds. While loading, a UI loading skeleton is displayed.</li> <li>/ssg: This page exports <code>prerender = true</code>. The comments displayed on this page are generated at build time, so posting them on the SSR page won’t be displayed here.</li> <li>/isr: An SSG page that uses incremental static regeneration. It exports the <code>isr</code> config with an expiration interval of 15 seconds. As explained in the previous section, this awesome feature allows updating our static page so that newly posted comments are reflected in our static page.</li> <li>/cache-headers: A minimal page that displays the current time. What we are experimenting with here is the cache headers. This page exports a Cache-Control header with a max-age of 10 seconds. Unlike ISR, the page is cached within the browser, not our deployment platform.</li></ul> <blockquote><p>Note: As mentioned in <a href="#" target="_blank" rel="noopener">Part 1</a>, we store and post our data to Supabase. The env file is shared in the GitHub repo, so you don’t have to worry about setting up your own. However, please remember to manually add them to the target platform when your app is deployed, as SvelteKit does not include them in your output.</p></blockquote> <h2>Deploying our app to Vercel</h2> <p>In this section, we will set up automatic deployment using <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app/tree/vercel" rel="noopener" target="_blank">vercel</a> and <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app/tree/vercel-edge" rel="noopener" target="_blank">vercel-edge</a> branches and set up a preview URL for each.</p> <h3>SvelteKit’s Vercel Adapter</h3> <p>To get your app deployed to Vercel, you must follow their file structure convention - <a href="https://vercel.com/docs/build-output-api/v3/configuration%5D" rel="noopener" target="_blank">Vercel’s build output API</a>; However, <code>@sveltejs/adapter-vercel</code> package does this automatically for you.</p> <p>We are going to use the adapter in our example app:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">pnpm</span> <span class="token function">install</span> @sveltejs/adapter-vercel</code><!----></pre> <p>Modifying <code>svelte.config.js</code> to use the adapter:</p> <pre class="language-jsx"><!----><code class="language-jsx"><span class="token keyword">import</span> adapter <span class="token keyword">from</span> <span class="token string">'@sveltejs/adapter-vercel'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> vitePreprocess <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'@sveltejs/kit/vite'</span><span class="token punctuation">;</span>

<span class="token comment">/** @type &#123;import('@sveltejs/kit').Config&#125; */</span>
<span class="token keyword">const</span> config <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	<span class="token literal-property property">preprocess</span><span class="token operator">:</span> <span class="token function">vitePreprocess</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

	<span class="token literal-property property">kit</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
		<span class="token literal-property property">adapter</span><span class="token operator">:</span> <span class="token function">adapter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> config<span class="token punctuation">;</span></code><!----></pre> <p>And finally, building our app while having the adapter configured:</p> <pre class="language-tsx"><!----><code class="language-tsx">pnpm build</code><!----></pre> <p>Expand the content of the <code>.vercel/output</code> directory. You will see a file structure tree similar to the following:</p> <pre class="language-bash"><!----><code class="language-bash">├── config.json
├── functions
│   └── fn.func
│       ├── node_modules
│       │   ├── @supabase
│       │   ├── @sveltejs
│       └── package.json
└── static
    ├── _app
    │   ├── immutable
    ├── favicon.png
    ├── ssg
    │   └── __data.json
    ├── isr
    │   └── __data.json
    ├── isr.html
    └── ssg.html</code><!----></pre> <p>This filesystem follows <a href="https://vercel.com/docs/build-output-api/v3/configuration" rel="noopener" target="_blank">Vercel’s build output API</a>, so deploying our app to Vercel will work naturally and as expected. Let’s have some highlights from the last tree snippet:</p> <ul><li>The file <code>config.json</code> is the core of our deployed app. It has the following properties: <ul><li><code>version</code>: The versions of the build output API being used. By the time of writing this blog post, it is using version 3.</li> <li><code>routes</code>: A set of regex patterns and destinations for every route defined in our SvelteKit app. Routes could have a source and destination and contain the returned status code, headers, etc. <ul><li>Note that the <code>"/_app/immutable/.+</code> source, exports a cache control header of 31536000 seconds (1 year). We will explain this within the current section.</li> <li>Note the <code>/.*"</code> source, which points at every route in our app. They all yield to a destination of <code>/fn/</code>, our serverless function. We will explain this here, too.</li></ul></li> <li><code>functions</code>: The place where our serverless functions are stored. Note that the Vercel adapters has mapped all of our routes to one function. This lets us gain some advantages: <ul><li>Reduce cold starts: When our app uses one serverless function, the cold starts will be drastically reduced, and the chance of it being warmed up will increase.</li> <li>Reduce costs: More serverless functions mean more costs. Combining multiple endpoints within one is more cost-effective.</li> <li>Better performance: The startup penalty is only paid once for all routes in our application.</li></ul></li> <li><code>static</code>: The place where our static assets and statically generated data are stored: <ul><li><code>_app/immutable</code>: Contains static files that will likely never change. It has a set of compiled JavaScript and CSS files, that’s why they are cached for 1 year, but they are fingerprinted so that when you update those without worrying about caching.</li></ul></li> <li>files with <code>ssg</code> and <code>isr</code> contain the data and HTML templates for your statically generated pages.</li></ul></li></ul> <h3>Connecting our example app to GitHub</h3> <p>It’s highly recommended to deploy our app using Git. Vercel supports deployments using <a href="https://github.com/" rel="noopener" target="_blank">GitHub</a>, <a href="https://about.gitlab.com/" rel="noopener" target="_blank">Gitlab</a>, and <a href="https://bitbucket.org/product" rel="noopener" target="_blank">Bitbucket</a>. In this blog post, we will use GitHub.</p> <p>Using VSCode, you could do this with a few keystrokes: press <strong>⌘+Shift+p</strong> (Mac) or <strong>Ctrl+Shift+p</strong> (Linux/Windows) and select <code>Publish to GitHub</code>.</p> <p><img src="/blog/mastering-sveltekit-deployment-3-github.gif" alt="publish to github"></p> <h3>Node Serverless runtime</h3> <p>We haven’t set any runtime options in our example app. In such case, Vercel will use the Serverless runtime by default. Let’s head over to <a href="https://vercel.com/" rel="noopener" target="_blank">Vercel</a> and get our app deployed!</p> <p>If you don’t have a Vercel account, create one. Adding a new project could be done by connecting your GitHub repo to Vercel. The rest will be automated for you!</p> <p><img src="/blog/mastering-sveltekit-deployment-3-deploy.gif" alt="Deploy to Vercel"></p> <p>Don’t forget to add the environment variables before deploying your app. All ends you need are included in the <code>.env</code> within the repo.</p> <blockquote><p>Note that the code for this section exists in the <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app/tree/vercel" rel="noopener" target="_blank">vercel</a> branch. Please fork the <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" rel="noopener" target="_blank">example app repo</a> and set it as your main branch before deployment. This is a limitation from Vercel. It will deploy your app initially using the main branch.</p></blockquote> <p>And finally, we get our app deployed!</p> <p><img src="/blog/mastering-sveltekit-deployment-3-deployed.gif" alt="Deployed app to Vercel using Node serverless runtime"></p> <h3>Edge Network</h3> <p>In earlier sections of this blog post, we have explained what Vercel’s Edge Runtime is. Now let’s experiment with and and deploy our example app to the edge!</p> <blockquote><p>If you have already forked the <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" rel="noopener" target="_blank">example app repo</a>, checkout the <code>vercel-edge</code> branch and set it as your main branch.</p></blockquote> <p>We are using the <code>runtime: 'edge'</code> option, which is completely supported by the Vercel adapter:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">import</span> adapter from <span class="token string">'@sveltejs/adapter-vercel'</span><span class="token punctuation">;</span>
<span class="token function">import</span> <span class="token punctuation">&#123;</span> vitePreprocess <span class="token punctuation">&#125;</span> from <span class="token string">'@sveltejs/kit/vite'</span><span class="token punctuation">;</span>

/** @type <span class="token punctuation">&#123;</span>import<span class="token punctuation">(</span><span class="token string">'@sveltejs/kit'</span><span class="token punctuation">)</span>.Config<span class="token punctuation">&#125;</span> */
const config <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	preprocess: vitePreprocess<span class="token punctuation">(</span><span class="token punctuation">)</span>,

	kit: <span class="token punctuation">&#123;</span>
		adapter: adapter<span class="token punctuation">(</span><span class="token punctuation">&#123;</span> runtime: <span class="token string">'edge'</span> <span class="token punctuation">&#125;</span><span class="token punctuation">)</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

<span class="token builtin class-name">export</span> default config<span class="token punctuation">;</span></code><!----></pre> <p>The <code>isr</code> page is not compatible with the edge runtime, so we will overwrite that behavior in a page specific config:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">import</span> <span class="token punctuation">&#123;</span> fetchComments <span class="token punctuation">&#125;</span> from <span class="token string">"<span class="token variable">$lib</span>/server/utils"</span><span class="token punctuation">;</span>
<span class="token function">import</span> <span class="token builtin class-name">type</span> <span class="token punctuation">&#123;</span> PageServerLoad <span class="token punctuation">&#125;</span> from <span class="token string">"./<span class="token variable">$types</span>"</span><span class="token punctuation">;</span>

<span class="token builtin class-name">export</span> const load: PageServerLoad <span class="token operator">=</span> async <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">&#123;</span>
	const comments <span class="token operator">=</span> await fetchComments<span class="token punctuation">(</span>fetch<span class="token punctuation">)</span><span class="token punctuation">;</span>

	<span class="token builtin class-name">return</span> <span class="token punctuation">&#123;</span>
		comments
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

<span class="token builtin class-name">export</span> const config <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	isr: <span class="token punctuation">&#123;</span>
		expiration: <span class="token number">15</span> // regenerate every <span class="token number">15</span> seconds
	<span class="token punctuation">&#125;</span>,
	runtime: <span class="token string">'nodejs20.x'</span> // Overwrite edge runtime
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span></code><!----></pre> <blockquote><p>Note: Some well-known node modules are not supported in the edge-runtime are unsupported. For example, we cannot access the file system using the <code>fs</code> module like in Node. But this is not a problem with our app since we don’t use it. We use <code>supabase</code> as our database which have great support for the edge runtime.</p></blockquote> <blockquote><p>Note that the code for this section exists in the <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app/tree/vercel-edge" rel="noopener" target="_blank">vercel-edge</a> branch.</p></blockquote> <h3>Deployment Notes</h3> <ul><li>As you see, the <code>ssr</code> and <code>ssr-streaming</code> are the most dynamic, whenever they are visited, they will have the most updated content.</li> <li>The <code>ssg</code> page is the least dynamic, the data you see displayed present in an HTML file obtained at build time.</li> <li>The <code>isr</code> are well supported by SvelteKit. this page has the good parts of the two worlds, it is regenerated based on an interval of 15 seconds, but you are likely to add longer intervals in real world apps.</li> <li>As expected, the <code>cahced-headers</code> page is updated each <code>10</code> seconds, which is the effect of the cache control header, but this behavior is browser specific since it’s cached there to prevent the request from the first place, if you visit the page on another browser for the first time, you will get new fresh content.</li></ul> <h3>Other options</h3> <p>Along with the mentioned <code>runtime</code> and <code>isr</code> options, <code>adapter-vercel</code> supports other options. Here are some to name a few:</p> <ul><li><code>split</code>: This option is set to <code>false</code> by default. If enabled, your SvelteKit loaders, actions, and API routes will be split across different serverless functions, but I wouldn’t recommend this unless there’s a specific thing you want to do.</li> <li><code>external</code>: This option only applies to <code>edge</code> routes. It allows setting a list of optional dependencies which are not compatible with the <code>edge runtime</code> (Node-specific modules).</li> <li>There are other options like <code>memory</code> and <code>maxDuration</code> that you can pass, but it’s unlikely that you will need them. For more info, check out <a href="https://kit.svelte.dev/docs/adapter-vercel" rel="noopener" target="_blank">SvelteKit’s docs</a></li></ul> <p>You have reached the end of this blog post. Thank you so much for reading! Next, we will pick another platform. We will deploy our SvelteKit app to <a href="https://www.cloudflare.com/" rel="noopener" target="_blank">CloudFlare</a>. See you!</p> <h3 class="text-gray-400">Useful Links:</h3> <ul><li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-1" target="_blank">Mastering SvelteKit Deployment Part 1: Getting Started</a></li> <li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" target="_blank">Mastering SvelteKit Deployment Part 2: Adapters, What Are They?</a></li> <li>Mastering SvelteKit Deployment Part 4: Deploying to CloudFlare (Comin soon!)</li> <li><a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" target="_blank">Example App GitHub repo</a></li></ul><!--]-->]]></content>
      </entry>
    <entry>
        <title>mastering-sveltekit-deployment-2</title>
        <link href="https://fayez.io/blog/mastering-sveltekit-deployment-2/"/>
        <id>https://fayez.io/blog/mastering-sveltekit-deployment-2/</id>
        <updated>2023-10-18T21:00:00.000Z</updated>
        <published>2023-10-18T21:00:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>In <a href="https://fayez.io/blog/mastering-sveltekit-deployment-1" rel="noopener" target="_blank">Part 1</a>, we have set up our example SvelteKit app, which we will deploy to multiple platforms throughout the series. Now, we will explore the concept of Adapters, their purpose, and how they internally work behind the scenes.</p> <h2>Building our SvelteKit App</h2> <p>To grasp the concept behind Adapters, we first need to study the output of compiling SvelteKit apps:</p> <pre class="language-bash"><!----><code class="language-bash">	<span class="token builtin class-name">cd</span> deployed-sveltekit-app
	<span class="token function">pnpm</span> build</code><!----></pre> <p>Running the Vite build command will output a <code>.svelte-kit</code> directory in the root of our project, which contains the following subdirectories:</p> <pre class="language-bash"><!----><code class="language-bash">	├── generated
	├── output
	└── types</code><!----></pre> <p>Our focus is the <code>output</code> directory, but let’s give a quick summary for each:</p> <ul><li>The <code>gnerated</code> directory is the first step in the build process, it has a fingerprinted hash for each build, and maps to our project to be used for generating the build output, it’s also used for watching changes for hot module replacements.</li> <li>Server type declaration files are stored in the <code>types</code> directory, which also outputs a JSON file which contains route-to-file mappings.</li> <li>We can say that the <code>output</code> directory is made from the penultimate step during the build process, it’s a set of JavaScript, HTML, and CSS files <span class="text-gray-600">which is what every Svelte app is compiled to</span>. It has both server code and client code, and contains <span class="text-gray-600">a manifest file that export regex patterns for our app routes</span>.</li></ul> <p>You can preview the build using the command <code>pnpm preview</code> or <code>vite preview</code>, but this command is not useful for deploying to deployment platforms, as they tend to be different from each other, they have different envs, runtimes, features, options, and many other differences that step in our way of deploying our SvelteKit app 🚶🏻</p> <h2>What Are Adapters?</h2> <p>We live in a world where we have a lot of different deployment platforms, in forms of serverful, serverless, static, and others. Deploying an app that works on a specific platform requires processing our output to follow their vendor-specific rules, which is a time consuming task. Adapters are available for us to automate all of this hard work, <strong>they add a build step for adapting our output to a target platform of choice</strong>.</p> <blockquote><p>Adapters are not taught to SvelteKit, other frameworks like Nuxt and Astro have technically the same concept that works in a similar pattern.</p></blockquote> <p>There are a lot of adapters available as ready-to-use packages, some examples are <code>adapter-node</code>, <code>adapter-static</code>, <code>adapter-vercel</code>, etc. You can even build your own if you don’t find what you are looking for.</p> <h2>The default Auto Adapter</h2> <p>SvelteKit comes out of the box with an adapter called <code>adaptera-auto</code> that determine which adapter to use by checking platform-specific environment variables, it installs and runs the one which it detects. Here’s a code snippet taken from <a href="https://github.com/sveltejs/kit/blob/master/packages/adapter-auto/adapters.js" rel="noopener" target="_blank">SvelteKit’s GitHub repo</a>:</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">export</span> <span class="token keyword">const</span> adapters <span class="token operator">=</span> <span class="token punctuation">[</span>
	<span class="token punctuation">&#123;</span>
		name<span class="token operator">:</span> <span class="token string">'Vercel'</span><span class="token punctuation">,</span>
		<span class="token function-variable function">test</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">!</span><span class="token operator">!</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">VERCEL</span><span class="token punctuation">,</span>
		module<span class="token operator">:</span> <span class="token string">'@sveltejs/adapter-vercel'</span><span class="token punctuation">,</span>
		version<span class="token operator">:</span> <span class="token string">'2'</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
	<span class="token punctuation">&#123;</span>
		name<span class="token operator">:</span> <span class="token string">'Cloudflare Pages'</span><span class="token punctuation">,</span>
		<span class="token function-variable function">test</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">!</span><span class="token operator">!</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">CF_PAGES</span><span class="token punctuation">,</span>
		module<span class="token operator">:</span> <span class="token string">'@sveltejs/adapter-cloudflare'</span><span class="token punctuation">,</span>
		version<span class="token operator">:</span> <span class="token string">'2'</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
	<span class="token punctuation">&#123;</span>
		name<span class="token operator">:</span> <span class="token string">'Netlify'</span><span class="token punctuation">,</span>
		<span class="token function-variable function">test</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">!</span><span class="token operator">!</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">NETLIFY</span><span class="token punctuation">,</span>
		module<span class="token operator">:</span> <span class="token string">'@sveltejs/adapter-netlify'</span><span class="token punctuation">,</span>
		version<span class="token operator">:</span> <span class="token string">'2'</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
	<span class="token punctuation">&#123;</span>
		name<span class="token operator">:</span> <span class="token string">'Azure Static Web Apps'</span><span class="token punctuation">,</span>
		<span class="token function-variable function">test</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">GITHUB_ACTION_REPOSITORY</span> <span class="token operator">===</span> <span class="token string">'Azure/static-web-apps-deploy'</span><span class="token punctuation">,</span>
		module<span class="token operator">:</span> <span class="token string">'svelte-adapter-azure-swa'</span><span class="token punctuation">,</span>
		version<span class="token operator">:</span> <span class="token string">'0.13'</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">,</span>
	<span class="token punctuation">&#123;</span>
		name<span class="token operator">:</span> <span class="token string">'AWS via SST'</span><span class="token punctuation">,</span>
		<span class="token function-variable function">test</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">!</span><span class="token operator">!</span>process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">SST</span><span class="token punctuation">,</span>
		module<span class="token operator">:</span> <span class="token string">'svelte-kit-sst'</span><span class="token punctuation">,</span>
		version<span class="token operator">:</span> <span class="token string">'2'</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span></code><!----></pre> <p>As noted, the adapter installs the detected target module. It doesn’t support all platforms and is too much abstracted, it even makes the build slower because it updates the packages lock-file outside of our repo, for those reasons, it’s highly recommended to use a specific adapter in the first place.</p> <h3>Exploring the Node Adapter</h3> <p>Let’s have an experiment with <code>adapter-node</code> and see the output it generates, we will explain deploying SvelteKit apps to node servers in a future part of the series. For now, our focus is to explain how Adapters work.</p> <p>Let’s begin by installing the adapter to our example app:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">pnpm</span> <span class="token function">install</span> <span class="token parameter variable">-D</span> @sveltejs/adapter-vercel</code><!----></pre> <p>If you haven’t already, open the example app in your favorite text editor:</p> <pre class="language-bash"><!----><code class="language-bash">code-insiders <span class="token builtin class-name">.</span></code><!----></pre> <p>And let’s move to <code>svelte.config.js</code> and use the new adapter there:</p> <pre class="language-jsx"><!----><code class="language-jsx"><span class="token keyword">import</span> adapter <span class="token keyword">from</span> <span class="token string">'@sveltejs/adapter-node'</span><span class="token punctuation">;</span> # We have only changed <span class="token keyword">this</span> line
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> vitePreprocess <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'@sveltejs/kit/vite'</span><span class="token punctuation">;</span>

<span class="token comment">/** @type &#123;import('@sveltejs/kit').Config&#125; */</span>
<span class="token keyword">const</span> config <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	<span class="token comment">// for more information about preprocessors</span>
	<span class="token literal-property property">preprocess</span><span class="token operator">:</span> <span class="token function">vitePreprocess</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

	<span class="token literal-property property">kit</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
		<span class="token literal-property property">adapter</span><span class="token operator">:</span> <span class="token function">adapter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> config<span class="token punctuation">;</span></code><!----></pre> <p>Let’s build our app again:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">pnpm</span> build</code><!----></pre> <p>The command will output a <code>.svelte-kit</code> directory similar to the one before, but this time it’s going to feed it with the node adapter and generate a new <code>build</code> directory that contains our node app.</p> <blockquote><p>There are a set of options we can pass to the node adapter, we are going to explain those in a later part of the series, for now we will use the defaults that work well for us.</p></blockquote> <p>You can expand the <code>build</code> directory to explore it’s content, you won’t see any sign of Svelte there, it’s a Fullstack Node.js application that’s deployable out of the box! you can even run it:</p> <pre class="language-bash"><!----><code class="language-bash">➜ <span class="token builtin class-name">export</span> <span class="token assign-left variable">ORIGIN</span><span class="token operator">=</span>http://localhost:3000

➜ <span class="token function">node</span> build <span class="token comment">#runs buid/index.js</span>

Listening on <span class="token number">0.0</span>.0.0:3000</code><!----></pre> <p>Why pass the <code>ORIGIN</code> env? Our app needs a way to know the base URL for server actions to work, otherwise we are going to get a 403 Forbidden response after each form submission.</p> <p>Head over to http://localhost:3000 and you will see our app sitting there!</p> <p><img src="/blog/mastering-sveltekit-deployment-2.gif" alt="Our example app running on the browser"></p> <p>As you see, the app is dynamic! Having a node server allowed us to use awesome features like SSR and Streaming. In addition to that, with the power of SvelteKit we had the ability to add some static pages to the website!</p> <h3>Getting into Adapter-Static</h3> <p>Now we will move a step further and explain pure SSG outputs, which is generated by using <code>adapter-static</code>, our app will turn into a complete static website, which means it won’t have server code anymore, and all what you get will be HTML, CSS, and JavaScipt files. Taking this approach allows you to deploy your app to services specifically designed for static content, like Static Hosting Providers or Static-Only CDNs. This can significantly reduce your costs if you don’t require a server for your website.</p> <p>Before we begin, I would like to quote some text with you, taken from the <a href="https://kit.svelte.dev/docs/page-options#prerender-when-not-to-prerender" rel="noopener" target="_blank">SvelteKit’s docs</a></p> <blockquote><p>The basic rule is this: for a page to be prerenderable, any two users hitting it directly must get the same content from the server.</p></blockquote> <p>We will follow this good rule in this section, the pages we will have are purely static without dynamic behavior, so we will get rid of any dynamic pages here, this is what <code>adapter-static</code> is meant for, pure static websites!</p> <p>Let’s start with installing the adapter:</p> <pre class="language-zsh"><!----><code class="language-zsh">pnpm install @sveltejs/adapter-static</code><!----></pre> <p>And as usual, modify <code>svelte.config.js</code> to use the newly installed adapter:</p> <pre class="language-js"><!----><code class="language-js"><span class="token keyword">import</span> adapter <span class="token keyword">from</span> <span class="token string">'@sveltejs/adapter-static'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> vitePreprocess <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'@sveltejs/kit/vite'</span><span class="token punctuation">;</span>

<span class="token comment">/** @type &#123;import('@sveltejs/kit').Config&#125; */</span>
<span class="token keyword">const</span> config <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	<span class="token literal-property property">preprocess</span><span class="token operator">:</span> <span class="token function">vitePreprocess</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

	<span class="token literal-property property">kit</span><span class="token operator">:</span> <span class="token punctuation">&#123;</span>
		<span class="token literal-property property">adapter</span><span class="token operator">:</span> <span class="token function">adapter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> config<span class="token punctuation">;</span></code><!----></pre> <p>Before building the app, there are few things we need to do:</p> <ul><li><p>Go to <code>+layout.server.ts</code> and enable pre-rendering for your whole app</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">import</span> <span class="token punctuation">&#123;</span> fetchAvatar <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'$lib/utils'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">&#123;</span> ServerLoad <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'@sveltejs/kit'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> prerender <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> load<span class="token operator">:</span> <span class="token function-variable function">ServerLoad</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">&#123;</span> fetch<span class="token punctuation">,</span> cookies <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
	<span class="token keyword">const</span> avatar <span class="token operator">=</span> cookies<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'avatar'</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token punctuation">(</span><span class="token keyword">await</span> <span class="token function">fetchAvatar</span><span class="token punctuation">(</span>fetch<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

	cookies<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'avatar'</span><span class="token punctuation">,</span> avatar<span class="token punctuation">)</span><span class="token punctuation">;</span>

	<span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
		avatar
	<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span></code><!----></pre></li> <li><p>In order for the website to be purely static, runtime server code needs to be removed, in our case, we remove all API handlers and server actions. So we will remove the <code>api/avatar</code>, <code>ssr</code>, <code>ssr-streaming</code> routes.</p></li> <li><p>Remove the on:click handler on the <code>AvatarButton</code> component as it fetches from a removed route.</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token operator">&lt;</span>script lang<span class="token operator">=</span><span class="token string">"ts"</span><span class="token operator">></span>
	<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> avatar <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'./stores'</span><span class="token punctuation">;</span>
	<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> fetchAvatar <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'./utils'</span><span class="token punctuation">;</span>

	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token function-variable function">setAvatar</span> <span class="token operator">=</span> <span class="token punctuation">(</span>value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
		$avatar <span class="token operator">=</span> value<span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

	<span class="token keyword">const</span> <span class="token function-variable function">generateAvatar</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">const</span> avatar <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetchAvatar</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token function">setAvatar</span><span class="token punctuation">(</span>avatar<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
<span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">></span>

<span class="token operator">&lt;</span>button on<span class="token operator">:</span>click<span class="token operator">=</span><span class="token punctuation">&#123;</span>generateAvatar<span class="token punctuation">&#125;</span><span class="token operator">></span>
	<span class="token operator">&lt;</span>img src<span class="token operator">=</span><span class="token punctuation">&#123;</span>$avatar<span class="token punctuation">&#125;</span> alt<span class="token operator">=</span><span class="token string">"avatar"</span> width<span class="token operator">=</span><span class="token punctuation">&#123;</span><span class="token number">48</span><span class="token punctuation">&#125;</span> height<span class="token operator">=</span><span class="token punctuation">&#123;</span><span class="token number">48</span><span class="token punctuation">&#125;</span> <span class="token operator">/</span><span class="token operator">></span>
<span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">></span>

<span class="token operator">&lt;</span>style<span class="token operator">></span>
		<span class="token operator">...</span>
<span class="token operator">&lt;</span><span class="token operator">/</span>style<span class="token operator">></span></code><!----></pre></li> <li><p>Lastly, we remove any links pointing to a removed routes, in such case, we remove links to <code>ssr</code> and <code>ssr-streaming</code> pages in <code>+layout.svelte</code>:</p> <pre class="language-svelte"><!----><code class="language-svelte"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
	<span class="token keyword">import</span> type <span class="token punctuation">&#123;</span> PageData <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'./$types'</span><span class="token punctuation">;</span>
	<span class="token keyword">import</span> HeaderLink <span class="token keyword">from</span> <span class="token string">'$lib/HeaderLink.svelte'</span><span class="token punctuation">;</span>
	<span class="token keyword">import</span> AvatarButton <span class="token keyword">from</span> <span class="token string">'$lib/AvatarButton.svelte'</span><span class="token punctuation">;</span>
	<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> avatar <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'$lib/stores'</span><span class="token punctuation">;</span>

	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token literal-property property">data</span><span class="token operator">:</span> PageData<span class="token punctuation">;</span>

	$avatar <span class="token operator">=</span> data<span class="token punctuation">.</span>avatar<span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span><span class="token namespace">svelte:</span>head</span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>Deployed SvelteKit app<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Example app for Mastering SvelteKit deployment series<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span><span class="token namespace">svelte:</span>head</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex items-center justify-between p-4 bg-gray-100<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HeaderLink</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Home<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HeaderLink</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HeaderLink</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/ssr<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>SSR<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HeaderLink</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HeaderLink</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/ssg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>SSG<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HeaderLink</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HeaderLink</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/ssr-streaming<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Streaming<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HeaderLink</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HeaderLink</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/ssg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>ISR<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HeaderLink</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HeaderLink</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/cache-headers<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Cache Headers<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HeaderLink</span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span>

	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>AvatarButton</span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span><span class="token punctuation">></span></span>
	<span class="token language-javascript"><span class="token punctuation">&#123;</span>#key data<span class="token punctuation">&#125;</span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>slot</span> <span class="token punctuation">/></span></span>
	<span class="token language-javascript"><span class="token punctuation">&#123;</span><span class="token operator">/</span>key<span class="token punctuation">&#125;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
	...
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span></code><!----></pre></li></ul> <p>And we finally get to build our app:</p> <pre class="language-zsh"><!----><code class="language-zsh">	pnpm build</code><!----></pre> <p>If you expand the content of the `build` directory, you will get a filesystem tree similar to the following:</p> <pre class="language-zsh"><!----><code class="language-zsh">	├── __data.json
	├── _app
	│ ├── immutable
	│ │ ├── assets
	│ │ │ ├── [..].css
	│ │ ├── chunks
	│ │ │ ├── [..].js
	│ │ ├── entry
	│ │ │ ├── [..].js
	│ │ └── nodes
	│ │ ├── [..].js
	├── cache-headers
	│ └── __data.json
	├── cache-headers.html
	├── favicon.png
	├── index.html
	├── ssg
	│ └── __data.json
	└── ssg.html</code><!----></pre> <p>As you see, our whole app turned into a set of HTML, CSS, JavaScript, and few JSON files storing already fetched data. You can explore the content of each directory, no trace of a single Nodejs function!</p> <p>But that’s too much, right? let’s talk details and dive into each directory:</p> <ul><li><code>_app/immutable</code>: Contains static files that are likely to never change, it has a set of compiled JavaScript and CSS files, that’s why they are cached for 1 year, but they are fingerprinted so that when you update those without worrying about caching.</li> <li><code>ssg</code>: For each route in our app, we have a generated HTML file and data JSON. For the root route /, we have the index.html and <strong>data.json, and for each other route, we will have them as <code>[route_name].html</code> and `[route_name]/</strong>data.json`. Those JSON files are simply the data generated by each route load function (here we only have the SSG page). It’s the static data being generated once at build time.</li> <li>We will also have our static assets in the same directory, for example, the favicon or any other static file you have, but those won’t be fingerprinted, and currently, SvelteKit does not cache them for you.</li></ul> <p>Now you can run the <code>build/index.html</code>, or better, fire up a simple HTTP server from the <code>build</code> directory, and it will work just like any static website!</p> <p><img src="/blog/mastering-sveltekit-deployment-2-static.gif" alt="Our static example website running on the browser"></p> <blockquote><p>Note that the ISR page may remain as an SSR page, as of writing this blog post, only Vercel supports this feature.</p></blockquote> <p>I hope you grasped this foundational blog post. If not, feel free to read it again or simply move forward! See you in the next one which covers deploying our app to Vercel.</p> <h3 class="text-gray-400">Useful Links:</h3> <ul><li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-1" target="_blank">Mastering SvelteKit Deployment Part 1: Getting Started</a></li> <li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-3" target="_blank">Mastering SvelteKit Deployment Part 3: Deploying to Vercel</a></li> <li><a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" target="_blank">Example App GitHub repo</a></li></ul><!--]-->]]></content>
      </entry>
    <entry>
        <title>mastering-sveltekit-deployment-1</title>
        <link href="https://fayez.io/blog/mastering-sveltekit-deployment-1/"/>
        <id>https://fayez.io/blog/mastering-sveltekit-deployment-1/</id>
        <updated>2023-10-16T21:00:00.000Z</updated>
        <published>2023-10-16T21:00:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>One of the great parts of SvelteKit is how it supports different platforms in terms of deployment. It’s built with Zero restrictions, and you can deploy it almost everywhere.</p> <p>Throughout this series, we will dive into various cloud-hosting services and explore our options. One exciting part about this journey is the vast knowledge you will gain about deployment platforms and what makes them different.</p> <h2>Outline</h2> <p>We will break down the series into the following parts:</p> <ul><li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-1" target="_blank">Part 1: Getting Started</a></li> <li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" target="_blank">Part 2: Adapters, What are they?</a></li> <li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-3" target="_blank">Part 3: Deploying to Vercel</a></li> <li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-4" target="_blank">Part 4: Deploying to Cloudflare</a></li> <li><a href="https://fayez.io/blog/mastering-sveltekit-deployment-5" target="_blank">Part 5: Deploying to AWS S3</a></li> <li>More to come, feel free to reach out for suggestions 🙂</li></ul> <p>When you decide to read a blog post on this series, I highly recommend applying what you learn. Doing so is the only way to learn and gain experience. You are free to skip parts as you see fit, but it would be best if you read <a href="https://fayez.io/blog/mastering-sveltekit-deployment-1" rel="noopener" target="_blank">Part 1</a> and <a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" rel="noopener" target="_blank">Part 2</a></p> <h2>The Example app</h2> <p>We will use a sample app throughout this series, showcasing different features of SvelteKit. We will go through SSR, streaming, server actions, prerendering, ISR, Pure SSG, API handlers, and more. This app allows users to view and post comments and change their avatar image.</p> <p>Go ahead and clone the <a href="https://github.com/fayez-nazzal/deployed-sveltekit-app" rel="noopener" target="_blank">GitHub repo</a>, which contains the example app we’re going to deploy:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">git</span> clone

code deployed-sveltekit-app <span class="token comment"># Or use your favorite editor</span></code><!----></pre> <p>We will be using Supabase for data storage. Setting up a supabase instance is pretty straightforward, but no worries! I made one for you. The public key is included within the repo 👀 - Remember it’s restricted. Feel free to set up your own for more freedom.</p> <p>You will find that the file structure for the example app looks similar to the following:</p> <pre class="language-zsh"><!----><code class="language-zsh">.
├── src
│   ├── app.d.ts
│   ├── app.html
│   ├── index.test.ts
│   ├── lib
│   │   ...
│   └── routes
│       ├── +layout.server.ts
│       ├── +layout.svelte
│       ├── +page.svelte
│       ├── api
│       │   └── avatar
│       │       └── +server.ts
│       ├── cache-headers
│       │   ├── +page.server.ts
│       │   └── +page.svelte
│       ├── ssg
│       │   ├── +page.server.ts
│       │   └── +page.svelte
│       ├── isr
│       │   ├── +page.server.ts
│       │   └── +page.svelte
│       ├── ssr
│       │   ├── +page.server.ts
│       │   └── +page.svelte
│       └── ssr-streaming
│           ├── +page.server.ts
│           └── +page.svelte
├── static
│   └── favicon.png
├── svelte.config.js
├── tests
│   └── test.ts
├── tsconfig.json
├── vite.config.ts</code><!----></pre> <p>Let’s break down our example app into the following parts:</p> <ul><li>+layout.svelte: The root layout has a navigation bar and the current page’s content. The navbar has a button to change the user avatar to a random one by firing an AJAX request to the <code>/api/avatar</code> endpoint.</li> <li>+layout.server.ts: Contains a server load function that serves the root layout. It also fetches an initial user avatar, which is saved to a cookie — Note that the cookie on the load function won’t work for pre-rendered pages since the page is determined at build time.</li> <li>ssr: <ul><li>+page.svelte: This page displays a list of comments that are server-side rendered. It also has a feature for the users to post new comments, which is done by a form server action.</li> <li>+page.server.ts: Contains the server action for posting new user comments. For our example app, those are saved to Supabase.</li></ul></li> <li>ssr-streaming: <ul><li>+page.svelte: Same as the SSR page, The only difference is that it’s charged up with Streaming supprt. It will delay fetching unnecessary data to enhance the experience. Here we delay fetching comments by 2 seconds to simulate this behavior.</li> <li>+page.server.ts: Similar to the SSR server load, the difference is that we make comments nested inside the object and omit the await, which allows us to stream the promise from the server.</li></ul></li> <li>ssg: <ul><li>+page.svelte: This page displays the same comments, but this time they are statically generated once at build time, it’s not dynamic data anymore, and this page doesn’t allow adding new comments.</li> <li>+page.ts: Exports a <code>prerender = true</code> option to enable static generation for this page.</li></ul></li> <li>ssg-sr: <ul><li>+page.svelte: Same as SSG page, but here it’s charged up with Incremental Static Regeneration. It will keep running the load function to update comments at a specific fixed interval.</li> <li>+page.ts: Exports a <code>config</code> object with the <code>isr</code> option, the expiration is set to 15 seconds. In real websites you would probably make it larger than this interval.</li></ul></li> <li>cache-headers: <ul><li>+page.svelte: A simple page that shows the current time in “HH:mm:ss” format. What’s interesting here is that this page is served with a cache header.</li> <li>+page.server.ts: Exports the current time with a <code>Cache-Control</code> header of <code>max-age=10</code>, which means that the time will be cached for 10 seconds.</li></ul></li> <li>/api/avatar: A GET request handler that generates a random avatar. It first generates a set of colors using the Node crypto API, then use those to return an imageSrc that points to the boringAvatars API.</li></ul> <p>As you see, we have integrated many features to experiment with. Note that not every feature will work on every platform, especially the server functions, which are not supported by static-only deployment platforms. We will dig deeper into this in the next parts.</p> <p><img src="/blog/mastering-sveltekit-deployment-1.gif" alt="Example App show"></p> <p>As we explore different deployment options, the example app may be modified. Each modified version will be in a separate branch based on the original example app, which is in the <code>main</code> branch.</p> <p>That’s all for the first part. In <a href="https://fayez.io/blog/mastering-sveltekit-deployment-2" rel="noopener" target="_blank">Part 2</a>, we will explain the concepts of adapters like never before! exciting things ahead! See you in the next one.</p><!--]-->]]></content>
      </entry>
    <entry>
        <title>sveltekit-view-transitions</title>
        <link href="https://fayez.io/blog/sveltekit-view-transitions/"/>
        <id>https://fayez.io/blog/sveltekit-view-transitions/</id>
        <updated>2023-09-01T00:00:00.000Z</updated>
        <published>2023-09-01T00:00:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>A few days ago, SvelteKit version 1.24.0 dropped with the new <code>onNavigate</code> lifecycle function, to support the <a href="https://developer.chrome.com/docs/web-platform/view-transitions" rel="noopener" target="_blank">Web View Transitions API</a>, which is a modern CSS-friendly way to handle page transitions.</p> <p>This blog post will cover the basics of setting up an example, which I published on this <a href="https://github.com/fayez-nazzal/sveltekit-view-transitions" rel="noopener" target="_blank">GitHub repo</a>.</p> <p>We will be creating a Pokemon dashboard app with a minimalist design, the app will have two pages. The first page shows the user info with a Pokemon image, and the second page allows editing the user info and the Pokemon ID, which is used for retrieving the image. Moving between pages will show <code>scale-in-out</code> transition effect. Let’s get started!</p> <p><img src="/blog/pokemon-dashboard-short.gif" alt="Pokemon Dashboard App"></p> <p>Create a new SvelteKit app using <code>pnpm</code>, or whatever package manager you prefer.</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">pnpm</span> create svelte@latest page-transitions</code><!----></pre> <p>This will create a new directory in your file system, let’s go there and install the packages we need:</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token builtin class-name">cd</span> page-transitions
<span class="token function">pnpm</span> <span class="token function">install</span>
<span class="token function">pnpm</span> <span class="token function">install</span> <span class="token parameter variable">-D</span> unocss @iconify-json/mdi

code <span class="token builtin class-name">.</span> <span class="token comment"># Open the project using your favorite text editor</span></code><!----></pre> <p>UnoCSS is an Atomic CSS library similar to TailwindCSS, but much easier to set up. It includes ready-to-go presets that we can directly use.</p> <p>Let’s head into <code>vite.config.ts</code> and add <code>UnoCSS</code> plugin:</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">import</span> <span class="token punctuation">&#123;</span> sveltekit <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'@sveltejs/kit/vite'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> UnoCSS <span class="token keyword">from</span> <span class="token string">'unocss/vite'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> defineConfig <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'vite'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">defineConfig</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
	plugins<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token function">sveltekit</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">UnoCSS</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code><!----></pre> <p>Create a <code>uno.config.ts</code> file in the root directory:</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">import</span> <span class="token punctuation">&#123;</span> defineConfig<span class="token punctuation">,</span> presetUno<span class="token punctuation">,</span> presetIcons<span class="token punctuation">,</span> presetWebFonts <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'unocss'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">defineConfig</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
	presets<span class="token operator">:</span> <span class="token punctuation">[</span>
		<span class="token function">presetUno</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
		<span class="token function">presetIcons</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
			collections<span class="token operator">:</span> <span class="token punctuation">&#123;</span>
				<span class="token function-variable function">mdi</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">'@iconify-json/mdi/icons.json'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span> <span class="token operator">=></span> i<span class="token punctuation">.</span>default<span class="token punctuation">)</span>
			<span class="token punctuation">&#125;</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
		<span class="token function">presetWebFonts</span><span class="token punctuation">(</span><span class="token punctuation">&#123;</span>
			provider<span class="token operator">:</span> <span class="token string">'google'</span><span class="token punctuation">,</span>
			fonts<span class="token operator">:</span> <span class="token punctuation">&#123;</span>
				sans<span class="token operator">:</span> <span class="token punctuation">&#123;</span>
					name<span class="token operator">:</span> <span class="token string">'Roboto'</span><span class="token punctuation">,</span>
					weights<span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">400</span><span class="token punctuation">,</span> <span class="token number">500</span><span class="token punctuation">]</span>
				<span class="token punctuation">&#125;</span>
			<span class="token punctuation">&#125;</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">)</span>
	<span class="token punctuation">]</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code><!----></pre> <p>UnoCSS is powered using presets, we are using the following official presets which are included in the main package:</p> <ul><li>presetUno: Allows us to use <code>TailwindCSS</code> classes in our project, this preset is also compatible with <code>windiCSS</code> classes.</li> <li>presetIcons: This preset is an awesome simple way to add icons using CSS classes, we will be using <code>@iconify-json/mdi</code> package for material design icons, only the icons we use will be added to our output bundle, nothing more <span class="inline-flex translate-y-px"><!----><svg viewBox="0 0 36 36" width="1em" height="1em" ><circle cx="18" cy="18" r="18" fill="#FFCC4D"/><path fill="#664500" d="M10.515 23.621C10.56 23.8 11.683 28 18 28s7.44-4.2 7.485-4.379a.5.5 0 0 0-.237-.554a.505.505 0 0 0-.6.077C24.629 23.163 22.694 25 18 25s-6.63-1.837-6.648-1.855a.5.5 0 0 0-.598-.081a.5.5 0 0 0-.239.557"/><ellipse cx="12" cy="13.5" fill="#664500" rx="2.5" ry="3.5"/><ellipse cx="24" cy="13.5" fill="#664500" rx="2.5" ry="3.5"/></svg><!----></span><!----></li> <li>presetWebFonts: For adding web fonts into our project. We will be using <code>Roboto</code> google font.</li></ul> <p>We have created our project and set it up, let’s move to the page transitions implementation.</p> <p>Create a parent layout file in your route directory: <code>routes/+layout.svelte</code> and add the following code:</p> <pre class="language-svelte"><!----><code class="language-svelte"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
	<span class="token keyword">import</span> <span class="token string">'virtual:uno.css'</span><span class="token punctuation">;</span>

	<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> onNavigate <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'$app/navigation'</span><span class="token punctuation">;</span>
	<span class="token keyword">import</span> HeaderItem <span class="token keyword">from</span> <span class="token string">'$lib/HeaderItem.svelte'</span><span class="token punctuation">;</span>

	<span class="token function">onNavigate</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
		<span class="token comment">// check browser compatibility</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>document<span class="token punctuation">.</span>startViewTransition<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>

		<span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">fulfil</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
			<span class="token comment">/* This will take a screenshot of the whole page, and freeze
					until the animation promise resolves */</span>
			document<span class="token punctuation">.</span><span class="token function">startViewTransition</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span>fulfil<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>h-screen flex flex-col font-sans font-medium text-white<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex justify-center gap-4 bg-sky p-2.5<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HeaderItem</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>i</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>i-mdi-view-dashboard w-6 h-6<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">></span></span>Dashboard<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HeaderItem</span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>HeaderItem</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/edit<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>i</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>i-mdi-account w-6 h-6<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">></span></span>Edit Profile<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>HeaderItem</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span>

	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col flex-grow min-h-0 content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>slot</span> <span class="token punctuation">/></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css">
	<span class="token comment">/* For animating the inner content only, without the nav-bar,
     we assign a unique transition name */</span>
	<span class="token selector">.content</span> <span class="token punctuation">&#123;</span>
		<span class="token property">view-transition-name</span><span class="token punctuation">:</span> content<span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>

	<span class="token comment">/* Set transition origin for old and new snapshots of the inner page */</span>
	<span class="token selector">:root::view-transition-old(content),
	:root::view-transition-new(content)</span> <span class="token punctuation">&#123;</span>
		<span class="token property">transform-origin</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>

	<span class="token comment">/* Animate the old snapshot out */</span>
	<span class="token selector">:root::view-transition-old(content)</span> <span class="token punctuation">&#123;</span>
		<span class="token property">animation</span><span class="token punctuation">:</span> 1s ease-in-out both scale-out-slide-left<span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>

	<span class="token comment">/* Animate the new snapshot in */</span>
	<span class="token selector">:root::view-transition-new(content)</span> <span class="token punctuation">&#123;</span>
		<span class="token property">animation</span><span class="token punctuation">:</span> 1s ease-in-out both slide-left-scale-in 0.5s<span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>

	<span class="token comment">/* Define the animations */</span>
	<span class="token atrule"><span class="token rule">@keyframes</span> scale-out-slide-left</span> <span class="token punctuation">&#123;</span>
		<span class="token selector">0%</span> <span class="token punctuation">&#123;</span>
			<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1<span class="token punctuation">)</span> <span class="token function">translateX</span><span class="token punctuation">(</span>0<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span>

		<span class="token selector">50%</span> <span class="token punctuation">&#123;</span>
			<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>0.8<span class="token punctuation">)</span> <span class="token function">translateX</span><span class="token punctuation">(</span>0<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span>

		<span class="token selector">100%</span> <span class="token punctuation">&#123;</span>
			<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>0.8<span class="token punctuation">)</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-150vw<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span>
	<span class="token punctuation">&#125;</span>

	<span class="token atrule"><span class="token rule">@keyframes</span> slide-left-scale-in</span> <span class="token punctuation">&#123;</span>
		<span class="token selector">0%</span> <span class="token punctuation">&#123;</span>
			<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>0.8<span class="token punctuation">)</span> <span class="token function">translateX</span><span class="token punctuation">(</span>150vw<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span>

		<span class="token selector">50%</span> <span class="token punctuation">&#123;</span>
			<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>0.8<span class="token punctuation">)</span> <span class="token function">translateX</span><span class="token punctuation">(</span>0<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span>

		<span class="token selector">100%</span> <span class="token punctuation">&#123;</span>
			<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1<span class="token punctuation">)</span> <span class="token function">translateX</span><span class="token punctuation">(</span>0<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span>
	<span class="token punctuation">&#125;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span></code><!----></pre> <p>This parent layout is the place where page transitions happens:</p> <ol><li>We begin by importing <code>virtual:uno.css</code>, this file is available after build time, which will serve as our Atomic CSS file for the styling to take effect.</li> <li>The onNavigate lifecycle function is the core part, it’s the place where all the magic happens, we first check browser compatibility. The <code>document.startViewTransition</code> is an asynchronous function that fires a callback as soon as the animation is done playing. It animates the page by taking a screenshot of the current page, and a live snapshot of the next page, the following CSS selectors will be valid for customizing the animation:</li></ol> <pre class="language-css"><!----><code class="language-css"><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">view-transition-old</span><span class="token punctuation">(</span>root<span class="token punctuation">)</span> <span class="token comment">/* old page snapshot */</span>
<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">view-transition-new</span><span class="token punctuation">(</span>root<span class="token punctuation">)</span> <span class="token comment">/* next page live snapshot */</span></code><!----></pre> <p>Those CSS selectors applies to the whole page, but it doesn’t stop here, you can assign unique view transition names for elements of your choice! We will explain that in just a moment.</p> <ol start="3"><li><p>The markup we have is straightforward, we have a header element that have links to the two pages we have, and each page is wrapped in a <code>div</code> element with a class name of <code>content</code>.</p></li> <li><p>Let’s explain what’s inside the <code>style</code> tag. In our example, the header element doesn’t change between pages, we want to animate the inner page only, we accomplish this by assigning a unique ID for the <code>div</code> element, using the <code>view-transition-name</code> CSS property.</p> <p>Which, in turn, takes snapshots of that inner page, the following CSS selectors will be available for us:</p> <pre class="language-css"><!----><code class="language-css"><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">view-transition-old</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token comment">/* old page snapshot */</span>
<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">view-transition-new</span><span class="token punctuation">(</span>content<span class="token punctuation">)</span> <span class="token comment">/* next page live snapshot */</span></code><!----></pre></li></ol> <p>We use those for specifying our own CSS animation, the old page will scale out and slide left, the new page will slide left and scale in.</p> <p>Let’s show the code for the missing elements, I will only give the file name with the code snippet. No need to explain those as they are beyond the point of this blog post.</p> <ul><li><p><code>routes/+layout.server.ts</code>:</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">&#123;</span> LayoutServerLoad <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'./$types'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> defaultData <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	name<span class="token operator">:</span> <span class="token string">'Hash Ketchum'</span><span class="token punctuation">,</span> <span class="token comment">// Ash Ketchum's cousin, he's a developer</span>
	email<span class="token operator">:</span> <span class="token string">'hash@catchemcode.com'</span><span class="token punctuation">,</span>
	pokemonId<span class="token operator">:</span> <span class="token number">7</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> load <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">&#123;</span> cookies <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
	<span class="token keyword">const</span> name <span class="token operator">=</span> cookies<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span> <span class="token operator">||</span> defaultData<span class="token punctuation">.</span>name<span class="token punctuation">;</span>
	<span class="token keyword">const</span> email <span class="token operator">=</span> cookies<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'email'</span><span class="token punctuation">)</span> <span class="token operator">||</span> defaultData<span class="token punctuation">.</span>email<span class="token punctuation">;</span>
	<span class="token keyword">const</span> pokemonId <span class="token operator">=</span> cookies<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'pokemonId'</span><span class="token punctuation">)</span> <span class="token operator">||</span> defaultData<span class="token punctuation">.</span>pokemonId<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

	cookies<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span>
	cookies<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'email'</span><span class="token punctuation">,</span> email<span class="token punctuation">)</span><span class="token punctuation">;</span>
	cookies<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'pokemonId'</span><span class="token punctuation">,</span> pokemonId<span class="token punctuation">)</span><span class="token punctuation">;</span>

	<span class="token keyword">const</span> paddedPokemonId <span class="token operator">=</span> pokemonId<span class="token punctuation">.</span><span class="token function">padStart</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token string">'0'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">const</span> pokemonAssetsUrl <span class="token operator">=</span> <span class="token string">'https://assets.pokemon.com/assets/cms2/img/pokedex/detail'</span><span class="token punctuation">;</span>
	<span class="token keyword">const</span> pokemonImageSrc <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>pokemonAssetsUrl<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>paddedPokemonId<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">.png</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">;</span>

	<span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
		pokemonImageSrc<span class="token punctuation">,</span>
		name<span class="token punctuation">,</span>
		email<span class="token punctuation">,</span>
		pokemonId
	<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">)</span> satisfies LayoutServerLoad<span class="token punctuation">;</span></code><!----></pre></li> <li><p><code>routes/+page.svelte</code>:</p> <pre class="language-svelte"><!----><code class="language-svelte"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
	<span class="token keyword">import</span> type <span class="token punctuation">&#123;</span> PageData <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'./$types'</span><span class="token punctuation">;</span>

	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token literal-property property">data</span><span class="token operator">:</span> PageData<span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex-grow min-h-0 bg-indigo-400<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mx-auto py-12 text-center<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-4x<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Welcome, <span class="token language-javascript"><span class="token punctuation">&#123;</span>data<span class="token punctuation">.</span>name<span class="token punctuation">&#125;</span></span>!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col items-center gap-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Your Pokeomon<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span>
				<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex items-center justify-center bg-gray-100 ring-5 ring-pink-400 p-3 rounded-full<span class="token punctuation">"</span></span>
			<span class="token punctuation">></span></span>
				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src=</span><span class="token language-javascript"><span class="token punctuation">&#123;</span>data<span class="token punctuation">.</span>pokemonImageSrc<span class="token punctuation">&#125;</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Avatar<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>w-64 h-64 rounded-full<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>
				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Your email<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span>
				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-normal underline<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token language-javascript"><span class="token punctuation">&#123;</span>data<span class="token punctuation">.</span>email<span class="token punctuation">&#125;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span></code><!----></pre></li> <li><p><code>routes/edit/+page.svelte</code>:</p> <pre class="language-svelte"><!----><code class="language-svelte"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
	<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> enhance <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'$app/forms'</span><span class="token punctuation">;</span>
	<span class="token keyword">import</span> TextField <span class="token keyword">from</span> <span class="token string">'$lib/TextField.svelte'</span><span class="token punctuation">;</span>
	<span class="token keyword">import</span> type <span class="token punctuation">&#123;</span> PageData <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'./$types.js'</span><span class="token punctuation">;</span>

	<span class="token keyword">export</span> <span class="token keyword">let</span> form<span class="token punctuation">;</span>
	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token literal-property property">data</span><span class="token operator">:</span> PageData<span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex-grow min-h-0 bg-violet-500<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mx-auto py-12 text-center<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-4x<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Edit Profile Info<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col items-center gap-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">method</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>POST<span class="token punctuation">"</span></span> <span class="token attr-name"><span class="token namespace">use:</span>enhance</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-center w-[90vw] lg:w-[50vw] xl:w-[33vw]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
				<span class="token language-javascript"><span class="token punctuation">&#123;</span>#<span class="token keyword">if</span> form<span class="token operator">?.</span>message<span class="token punctuation">&#125;</span></span>
					<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><span class="token language-javascript"><span class="token punctuation">&#123;</span>form<span class="token punctuation">.</span>message<span class="token punctuation">&#125;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span>
				<span class="token language-javascript"><span class="token punctuation">&#123;</span><span class="token operator">/</span><span class="token keyword">if</span><span class="token punctuation">&#125;</span></span>

				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>User Info<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span>
				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col gap-6<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
					<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TextField</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>name<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span> <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Name<span class="token punctuation">"</span></span> <span class="token attr-name">value=</span><span class="token language-javascript"><span class="token punctuation">&#123;</span>form<span class="token operator">?.</span>name <span class="token operator">??</span> data<span class="token punctuation">.</span>name<span class="token punctuation">&#125;</span></span> <span class="token punctuation">/></span></span>
					<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TextField</span>
						<span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span>
						<span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>email<span class="token punctuation">"</span></span>
						<span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Email<span class="token punctuation">"</span></span>
						<span class="token attr-name">value=</span><span class="token language-javascript"><span class="token punctuation">&#123;</span>form<span class="token operator">?.</span>email <span class="token operator">??</span> data<span class="token punctuation">.</span>email<span class="token punctuation">&#125;</span></span>
					<span class="token punctuation">/></span></span>
					<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TextField</span>
						<span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>pokemonId<span class="token punctuation">"</span></span>
						<span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>number<span class="token punctuation">"</span></span>
						<span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Pokemon Id<span class="token punctuation">"</span></span>
						<span class="token attr-name">value=</span><span class="token language-javascript"><span class="token punctuation">&#123;</span>form<span class="token operator">?.</span>pokemonId <span class="token operator">??</span> data<span class="token punctuation">.</span>pokemonId<span class="token punctuation">&#125;</span></span>
					<span class="token punctuation">/></span></span>
					<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span>
						<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mt-2 cursor-pointer bg-pink-400 hover:brightness-110 transition-all p-3 border-0 rounded-xl text-lg text-white<span class="token punctuation">"</span></span>
					<span class="token punctuation">></span></span>
						Save
					<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span>
				<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
			<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">></span></span>
		<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span></code><!----></pre></li> <li><p><code>routes/edit/+page.server.ts</code>:</p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">import</span> <span class="token punctuation">&#123;</span> fail <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'@sveltejs/kit'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">&#123;</span> Actions <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'./$types'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> actions <span class="token operator">=</span> <span class="token punctuation">&#123;</span>
	<span class="token function-variable function">default</span><span class="token operator">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">&#123;</span> request<span class="token punctuation">,</span> cookies <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword">await</span> request<span class="token punctuation">.</span><span class="token function">formData</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

		<span class="token keyword">const</span> name <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
		<span class="token keyword">const</span> email <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'email'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
		<span class="token keyword">const</span> pokemonId <span class="token operator">=</span> data<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'pokemonId'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>name <span class="token operator">||</span> <span class="token operator">!</span>email <span class="token operator">||</span> <span class="token operator">!</span>pokemonId<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
			<span class="token keyword">return</span> <span class="token function">fail</span><span class="token punctuation">(</span><span class="token number">400</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span>
				message<span class="token operator">:</span> <span class="token string">'Please fill out all fields'</span><span class="token punctuation">,</span>
				name<span class="token punctuation">,</span>
				email<span class="token punctuation">,</span>
				pokemonId
			<span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">&#125;</span>

		cookies<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'name'</span><span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span>
		cookies<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'email'</span><span class="token punctuation">,</span> email<span class="token punctuation">)</span><span class="token punctuation">;</span>
		cookies<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">'pokemonId'</span><span class="token punctuation">,</span> pokemonId<span class="token punctuation">)</span><span class="token punctuation">;</span>

		<span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
			message<span class="token operator">:</span> <span class="token string">'Your profile has been updated!'</span><span class="token punctuation">,</span>
			name<span class="token punctuation">,</span>
			email<span class="token punctuation">,</span>
			pokemonId
		<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span> satisfies Actions<span class="token punctuation">;</span></code><!----></pre></li> <li><p><code>lib/HeaderItem.svelte</code>:</p> <pre class="language-svelte"><!----><code class="language-svelte"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
	<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> page <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'$app/stores'</span><span class="token punctuation">;</span>

	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token literal-property property">href</span><span class="token operator">:</span> string<span class="token punctuation">;</span>

	<span class="token literal-property property">$</span><span class="token operator">:</span> isActive <span class="token operator">=</span> $page<span class="token punctuation">.</span>url<span class="token punctuation">.</span>pathname <span class="token operator">===</span> href<span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span>
	<span class="token attr-name">class="flex</span> <span class="token attr-name">items-center</span> <span class="token attr-name">gap-2</span> <span class="token attr-name">text-xl</span> <span class="token attr-name">color-white</span> <span class="token attr-name"><span class="token namespace">hover:</span>bg-cyan-100</span>/<span class="token attr-name">20</span> <span class="token attr-name">p-2.5</span> <span class="token attr-name">rounded-xl</span> <span class="token language-javascript"><span class="token punctuation">&#123;</span>isActive <span class="token operator">&amp;&amp;</span>
		<span class="token string">'bg-cyan-100/40'</span><span class="token punctuation">&#125;</span></span><span class="token attr-name">"</span>
	<span class="token language-javascript"><span class="token punctuation">&#123;</span>href<span class="token punctuation">&#125;</span></span>
<span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>slot</span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code><!----></pre> <p><code>lib/TextField.svelte</code>:</p> <pre class="language-svelte"><!----><code class="language-svelte"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token literal-property property">type</span><span class="token operator">:</span> string<span class="token punctuation">;</span>
	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token literal-property property">value</span><span class="token operator">:</span> string<span class="token punctuation">;</span>
	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token literal-property property">name</span><span class="token operator">:</span> string<span class="token punctuation">;</span>
	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token literal-property property">placeholder</span><span class="token operator">:</span> string<span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span>
	<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>p-3 rounded-xl drop-shadow outline-none border-none ring-3 ring-transparent focus:ring-pink-400 focus:border-pink-400 transition-all text-lg<span class="token punctuation">"</span></span>
	<span class="token language-javascript"><span class="token punctuation">&#123;</span>name<span class="token punctuation">&#125;</span></span>
	<span class="token language-javascript"><span class="token punctuation">&#123;</span>type<span class="token punctuation">&#125;</span></span>
	<span class="token language-javascript"><span class="token punctuation">&#123;</span>placeholder<span class="token punctuation">&#125;</span></span>
	<span class="token language-javascript"><span class="token punctuation">&#123;</span>value<span class="token punctuation">&#125;</span></span>
<span class="token punctuation">/></span></span></code><!----></pre></li></ul> <p>Now we have implemented a page transition without the hassale of using a third-party library or complex state management! This is the power of the new web transition API, and it’s awesome to see frameworks like SvelteKit and Astro adapting to it so quickly. Can’t wait to see more browserss supporting it ( Firefox, I’m looking at you <span class="inline-flex translate-y-px"><!----><svg viewBox="0 0 36 36" width="1em" height="1em" ><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18c-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"/><path fill="#664500" d="M25.485 29.879C25.44 29.7 24.317 25.5 18 25.5s-7.44 4.2-7.485 4.379a.5.5 0 0 0 .237.554a.51.51 0 0 0 .6-.077c.019-.019 1.954-1.856 6.648-1.856s6.63 1.837 6.648 1.855a.5.5 0 0 0 .598.081a.5.5 0 0 0 .239-.557m-9.778-12.586C12.452 14.038 7.221 14 7 14a1.001 1.001 0 0 0-.001 2c.029 0 1.925.022 3.983.737c-.593.64-.982 1.634-.982 2.763c0 1.934 1.119 3.5 2.5 3.5s2.5-1.566 2.5-3.5c0-.174-.019-.34-.037-.507c.013 0 .025.007.037.007a.999.999 0 0 0 .707-1.707M29 14c-.221 0-5.451.038-8.707 3.293A.999.999 0 0 0 21 19c.013 0 .024-.007.036-.007c-.016.167-.036.333-.036.507c0 1.934 1.119 3.5 2.5 3.5s2.5-1.566 2.5-3.5c0-1.129-.389-2.123-.982-2.763A14 14 0 0 1 29.002 16A1 1 0 0 0 29 14"/></svg><!----></span><!----> ).</p> <br> <p>I hope you enjoyed this blog post, and see you in the next one!</p><!--]-->]]></content>
      </entry>
    <entry>
        <title>embed-tweets-sveltekit</title>
        <link href="https://fayez.io/blog/embed-tweets-sveltekit/"/>
        <id>https://fayez.io/blog/embed-tweets-sveltekit/</id>
        <updated>2023-08-24T21:00:00.000Z</updated>
        <published>2023-08-24T21:00:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>In this blog post, I will provide you with a better way to embed tweets in your SvelteKit app, The tweet will be generated statically on the server. Without any additional scripts or iframe elements, and without sacrificing performance or having layout shifts anymore.</p> <p>In the last month, Vercel announced the new <a href="https://www.npmjs.com/package/react-tweet" rel="noopener" target="_blank">react-tweet</a> package for rendering statically generated tweets in your Next.js app. In the pages directory, it works by requesting the Twitter syndication API server-side before the page load. For React Server Components, the tweet is fetched and rendered server-side in the relevant component.</p> <p>I thought making a SvelteKit version would be cool! And I wanted to use the same on my blog, hence, I made the <a href="https://www.npmjs.com/package/sveltekit-tweet" rel="noopener" target="_blank">sveltekit-tweet</a> package.</p> <p>Let’s dive into the implementation details to get it running on your website. We will begin with the most basic example, then we will head into adding tweets to your markdown files.</p> <p>Begin by installing the `sveltekit-tweet`` package via pnpm - or npm/yarn, depending on your flavor of choice.</p> <pre class="language-bash"><!----><code class="language-bash"><span class="token function">pnpm</span> <span class="token function">add</span> sveltekit-tweet@latest</code><!----></pre> <p>And in <code>+page.server.ts</code>, we import the <code>getTweet</code> function from <code>sveltekit-tweet/server</code></p> <pre class="language-ts"><!----><code class="language-ts"><span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">&#123;</span> PageServerLoad <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'./$types'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> getTweet <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'sveltekit-tweet/server'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> load <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
	<span class="token keyword">const</span> tweetId <span class="token operator">=</span> <span class="token string">'1694201062717034868'</span><span class="token punctuation">;</span> <span class="token comment">// You'll find it in the tweet URL</span>
	<span class="token keyword">const</span> tweet <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">getTweet</span><span class="token punctuation">(</span>tweetId<span class="token punctuation">)</span><span class="token punctuation">;</span>

	<span class="token keyword">return</span> <span class="token punctuation">&#123;</span> tweet <span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">)</span> satisfies PageServerLoad<span class="token punctuation">;</span></code><!----></pre> <p>The tweetId could be directly extracted from the URL you want to embed, and the <code>getTweet</code> function will fetch the tweet info using Twitter’s / X’s API. This function is a server-only function, it won’t be imported into your client-side script (which is a good thing).</p> <p>The last step is to render what you get in your +page.svelte component:</p> <pre class="language-svelte"><!----><code class="language-svelte"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
	<span class="token keyword">import</span> <span class="token punctuation">&#123;</span> Tweet <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'sveltekit-tweet'</span><span class="token punctuation">;</span>
	<span class="token keyword">import</span> type <span class="token punctuation">&#123;</span> PageData <span class="token punctuation">&#125;</span> <span class="token keyword">from</span> <span class="token string">'./$types'</span><span class="token punctuation">;</span>

	<span class="token keyword">export</span> <span class="token keyword">let</span> <span class="token literal-property property">data</span><span class="token operator">:</span> PageData<span class="token punctuation">;</span>

	<span class="token keyword">const</span> tweet <span class="token operator">=</span> data<span class="token punctuation">.</span>tweet<span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Tweet</span> <span class="token language-javascript"><span class="token punctuation">&#123;</span>tweet<span class="token punctuation">&#125;</span></span> <span class="token punctuation">/></span></span></code><!----></pre> <br> <span class="text-2xl">Result:</span> <div data-tweet="1694201062717034868"></div> <p>Looks good, right? Now let’s head into embedding tweets in your markdown files, which is the cooler and more usable option. If you use a tool like mdsvex, you can add tweets to your articles this way:</p> <pre class="language-md"><!----><code class="language-md"><span class="token blockquote punctuation">></span> A quoted text followed by a tweet:

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">data-tweet</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1694201062717034868<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code><!----></pre> <p>The only requirement is to use the <code>renderTweets</code> function, in <code>+page.server.ts</code> we have the following:</p> <pre class="language-typescript"><!----><code class="language-typescript"><span class="token keyword">export</span> <span class="token keyword">const</span> load<span class="token operator">:</span> <span class="token function-variable function">PageServerLoad</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">&#123;</span> params <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">&#123;</span>
	<span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
		<span class="token comment">// Import the markdown file</span>
		<span class="token keyword">const</span> post <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">../blog/posts/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>params<span class="token punctuation">.</span>slug<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token string">.md</span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>

		<span class="token comment">// Render markdown file into HTML</span>
		<span class="token keyword">let</span> content <span class="token operator">=</span> <span class="token function">render</span><span class="token punctuation">(</span>post<span class="token punctuation">.</span>default<span class="token punctuation">)</span><span class="token punctuation">.</span>body<span class="token punctuation">;</span>

		<span class="token comment">// This will render all tags with data-tweet=”…” to statically generated tweets</span>
		content <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">renderTweets</span><span class="token punctuation">(</span>content<span class="token punctuation">,</span> fetchedTweets<span class="token punctuation">)</span><span class="token punctuation">;</span>

		<span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
			content<span class="token punctuation">,</span>
			meta<span class="token operator">:</span> post<span class="token punctuation">.</span>metadata<span class="token punctuation">,</span>
			slug<span class="token operator">:</span> params<span class="token punctuation">.</span>slug
		<span class="token punctuation">&#125;</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
		<span class="token keyword">throw</span> <span class="token function">error</span><span class="token punctuation">(</span><span class="token number">404</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">&#96;</span><span class="token string">Could not find </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">$&#123;</span>params<span class="token punctuation">.</span>slug<span class="token interpolation-punctuation punctuation">&#125;</span></span><span class="token template-punctuation string">&#96;</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span class="token punctuation">;</span></code><!----></pre> <p>The <code>renderTweets</code> function will transform all tags with <code>data-tweet</code> attribute be rendered using @html expression:</p> <pre class="language-svelte"><!----><code class="language-svelte"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript">
	<span class="token keyword">export</span> <span class="token keyword">let</span> data<span class="token punctuation">;</span>
</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
	<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>...<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token language-javascript"><span class="token punctuation">&#123;</span>@html data<span class="token punctuation">.</span>content<span class="token punctuation">&#125;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span></code><!----></pre> <br> <span class="text-2xl">Result:</span> <blockquote><p>A quoted text followed by a tweet:</p></blockquote> <div data-tweet="1694201062717034868"></div> <p>And that’s it! Now you can embed without having to worry anymore, and without any headache or poor performance metrics.</p><!--]-->]]></content>
      </entry>
    <entry>
        <title>is-svelte-the-game-changer-we-were-looking-for</title>
        <link href="https://fayez.io/blog/is-svelte-the-game-changer-we-were-looking-for/"/>
        <id>https://fayez.io/blog/is-svelte-the-game-changer-we-were-looking-for/</id>
        <updated>2023-08-16T21:00:00.000Z</updated>
        <published>2023-08-16T21:00:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>Svelte is a compact Frontend framework and UI definition language for crafting user interfaces for the web. It was created by Rich Harris in 2016, and it was increasing in popularity since then, especially in the last few years when developers have begun noticing React’s drawbacks. It even got its own rockstar meta-framework: SvelteKit, the Svelte counterpart to React’s Next.js. And now both are endorsed by the same company, Vercel.</p> <p>Svelte’s meta-framework, SvelteKit, reached its stable 1.0 release last year, and it offers a set of powerful features like mixed rendering patterns, nested layouts, server actions, and progressive enhancements, along with other useful features that contribute to a better user experience. According to Rich Harris - the creator of Svelte and SvelteKit, the team will now direct their attention to Svelte itself. So too, this article will focus on the impact and potential of Svelte in the web industry.</p> <p>Since the beginning of the web era, it became well known to folks that, to build a website, they use the three couples <code>HTML</code>, <code>CSS</code>, and <code>JavaScript</code>. Even people who want to dive into the web world start their journey by learning those technologies, which is an excellent starting point. However, building highly interactive and visually appealing interfaces by only using those base technologies is challenging and often difficult to maintain. Developer teams have always been hardworking to simplify the web, which leads to the adoption of <code>JQuery</code>, and, subsequently, the most-popular library for building user interfaces - <code>React</code>.</p> <p>The general paradigm is that JavaScript is event-driven. It reacts to events like user interaction, network calls, timers, and more. While UI is state-driven, it reflects the current state of the application or user interactions, This inconsistency led to unnecessary complexities, which is why the primary focus of React is to make JacaScript more state-driven. This is what has made it the most popular Frontend UI library, <strong>React changed how we build applications for the web, in a good way</strong>. ****</p> <p>The internal concept behind React is to abstract the DOM away for the users and use a Virtual DOM to detect changes in the UI, detect changes in the component state and update the UI accordingly, but this also leads to other drawbacks, having a virtual DOM is another layer of complexity, it often leads to unnecessary renders, and having tools like <code>useMemo</code> and <code>useCallback</code> is also an additional concern, adding complexity and leading to buggy UI, and those were invented to address the problem of unnecessary renders, which is React’s problem, not us.</p> <p>When Svelte came out, its primary goal was to show developers that we achieve the same with less complexity, and without additional costs like having the VirtualDom. Svelte is also event-driven, but unlike React, it works by augmenting HTML with JavaScript using DSLs - or magic!</p> <p>Svelte adds a compile step so that we are only deploying HTML, CSS, and JavaScript in the end, which is a big advantage over React. The end result of deploying a Svelte app is that the website is mereley as the browser expects, without any unnecessary work and overhead.</p> <p>The state-driven nature of Svelte is accomplished by making state assignments Reactive. Updates in the state reflect updates in the UI in the exact place where it’s needed, without any unnecessary work. <strong>The ultimate result is a front-end framework that has two impressive qualities: high performance and simplicity</strong>.</p> <p>That seems it’s exactly what the web was intended to be, and it is. Svelte is a versatile choice for building modern websites, but it’s a bit late - React has become the go-to option and the frontend choice to lead the web, it already established itself being the go-to choice for most web apps. React now leads the game having it’s own large community and the trust of many prominent companies to use it in their production web applications.</p> <p>Here is a tweet from <a href="https://twitter.com/steventey" rel="noopener" target="_blank"><code>@steventey</code></a> which has a subset of what React is used for:</p> <div data-tweet="1691877528653197333"></div> <p>So if you are learning web development and you want to pick your first UI library, or you are looking to start a new web app business, picking React is a great safe option, especially with modern meta frameworks like <code>Next.js</code> and <code>Remix</code>.</p> <p>Otherwise, if you are just starting with the web or are building your own personal website, I highly recommend picking Svelte, it’s closer to HTML and is much simpler and enjoyable to use for this kind of project. I had a great experience using SvelteKit for building my personal website.</p> <p>If you have an existing project using React or other tools, don’t feel behind the track, your framework is fine and there is no need to switch for most cases. A good reason to learn or use technologies like Svelte is for experimenting or obtaining new perspectives, and if you find that it was a great handy experience, don’t be afraid to pick it for larger projects, in the end, Vercel bets on Svelte the same way as it bets on React.</p><!--]-->]]></content>
      </entry>
    <entry>
        <title>engineer-vs-frameworker</title>
        <link href="https://fayez.io/blog/engineer-vs-frameworker/"/>
        <id>https://fayez.io/blog/engineer-vs-frameworker/</id>
        <updated>2023-08-07T21:00:00.000Z</updated>
        <published>2023-08-07T21:00:00.000Z</published>
        <content type="html"><![CDATA[<!--[--><p>When I begin my journey as a Frontend developer, I heard a piece of advice from my Senior developer that always stuck in my head.</p> <blockquote><p>Coding the design and implementing it is the easiest part. The real key point that makes you a better developer is your problem-solving skills.</p></blockquote> <p>As time goes on, I am starting to understand the importance of this legendary advice. We often see those high-tier developers and their impressive solutions. Without their exceptional problem-solving abilities, we would not have access to the powerful technologies we use daily.</p> <p>Those type of people who have solid problem-solving skills often master the Engineering mindset. They start by defining the problem, they look for a solution that works, and keep improving with better and more efficient solutions.</p> <p>On the other hand, there are many people who don’t have that engineering mindset, they are more picky about the technologies they use. When a new tool is released, they get hyped about it and often label their previous tools as ‘outdated’. They also tend to abandon or redo their side projects using the same excuse.</p> <p>The latter type of individuals often lack that Engineering mindset. They are more like frameworkers who prioritize new technologies over practicality. They are more modern-like developers, but tend to lack consistency in their lives. While they may perform amazingly at first because of the excitement with the new tools, they often lose interest and start looking for the next big thing.</p> <h2>The fact: Neither of those two is better than the other</h2> <p>Based on my tone, you may have noticed that I tend towards engineering thinking as the best or the smore practical approach. That’s true, but actually <strong>it depends</strong>.</p> <p>In the last two decades, we have seen a lot of companies almost dying because of their legacy systems, it doesn’t matter if their staff is great engineers or problem solvers, if they remain outdated, it’s over.</p> <p>Let’s take a look at Yahoo! for example, it had one of the most known websites of the internet era, but its staff struggled a lot to keep themselves up to date with the rapidly evolving technologies. They clearly failed on realizing the power that new solutions could bring to their search engine. On the other hand, Google saw the real power of modern tech, they have built powerful cloud servers, and web applications, and even integrated the power of AI &amp; Machine Learning into their search engine.</p> <p>We see a lot of engineers dropping their journey because they weren’t up to date, the same way as we see frameworkers dropping their journey because lack of problem-solving skills, what’s the solution? Find a middle ground!</p> <h2>How to be a more creative and modern engineer?</h2> <p>If you are from the first type, and you find yourself way behind those hyped frameworkers that you daily see on social media platforms, here is a list of tips you can follow:</p> <ol><li>Stay Updated: Keep up with new technologies and frameworks.</li> <li>Be Adaptable: Learn different tools that can simplify your tasks.</li> <li>Practical Learning: Try out new frameworks via hands-on projects.</li> <li>Don’t Reinvent: Avoid building from scratch when a solution already exists.</li></ol> <h2>How to improve your engineering thinking?</h2> <p>If you find yourself closer to the frameworker mindset, here is a list of tips that you can work on to improve your engineering thinking:</p> <ol><li>Understand Fundamentals: Learn the core engineering principles.</li> <li>Problem-Solving: Focus on developing your problem-solving skills.</li> <li>Look Under the Hood: Learn how frameworks work internally.</li> <li>Versatility: Don’t rely solely on a single framework; diversify your toolkit.</li></ol> <h2>Final thoughts</h2> <p>Being a better developer is not being that hard-core engineer, nor being that die-hard frameworker with the “tool of the next era” nonsense.</p> <p>It’s becoming a Tech-Balancer, who knows when to go old-school style and when to ride the waves of the latest tech!</p> <p>So go ahead, and make some magic happen 🙂</p><!--]-->]]></content>
      </entry>
  </feed>