Our great sponsors
-
svelte-sanity-tonotdo
An example of a To (Not) Do List application using Svelte, TypeScript, and Sanity
Note: If you want to follow along with styling, you can replace public/globals.css with the full example code, and also pull contents from individual component, like this one.
Render (Static) Inaction Cards
To bring it to life on the home page, adjust the default intro copy and render a few inactions from the main
src/App.svelte
component.
</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="nt">
To Don't List
class="intro">Here is a list of things I'm not going to do: title="Return VHS to Blockbuster" notes="You've had it since 1998. What's the rush?" /> title="Fix the hole in the wall" notes="Keeping it where it is adds character and nostalgia." /> title="Do the dishes" notes="Someone else will probably get to them. Just leave them in the sink." />Enter fullscreen mode Exit fullscreen modeTake a look back at your browser and you should see the list! (Refresh if you don't.)
There we go! We have a (very simple) Svelte app with just HTML, CSS, and JavaScript.
Adding TypeScript to a Svelte project
Now, suppose we wanted to add a due date. It's not too difficult in this example, right? Let's do it!
We add a new property to the Inaction Card component:
</span> <span class="k">export</span> <span class="kd">let</span> <span class="nx">title</span><span class="p">,</span> <span class="nx">notes</span><span class="p">,</span> <span class="nx">dueDate</span><span class="p">;</span> <span class="nt"> class="inaction"> class="title">{title} class="due-date">{dueDate} class="notes">{notes}
Enter fullscreen mode Exit fullscreen modeAnd to the inactions we render, we can add the new prop:
title="Return VHS to Blockbuster" dueDate={new Date("1992-02-29")} notes="You've had it since 1998. What's the rush?" /> title="Fix the hole in the wall" dueDate={Date.now()} notes="Keeping it where it is adds character and nostalgia." /> title="Do the dishes" dueDate={new Date("2024-02-29")} notes="Someone else will probably get to them. Just leave them in the sink." />
Enter fullscreen mode Exit fullscreen modeThen jump back over to your browser:
Notice anything weird? I do. Two things, actually:
- The first and the third inactions display an unnecessarily-long date. That could have been easily formatted, but we're not going to worry about that here.
- The second date is a number. That's a bigger issue, and I'll explain why.
What if, we rendered the type of
dueDate
instead? (i.e.typeof dueDate
)We get
number
for the second one! Even with this small of an application that could absolutely be an issue. Suppose you wanted to better control the formatting of the date. You'd have to first introspect the type of date and then format appropriately. It'd be much easier if we know (with confidence) what type to expect.TypeScript Saves the Day!
We can use TypeScript to tell our application exactly what we expect in
dueDate
.Since our Svelte app is still largely untouched, we can use the built-in utility to convert it to TypeScript. Shut down your development server (
ctrl
+c
), and then run the following commands:
$ node scripts/setupTypeScript.js $ npm install
Enter fullscreen mode Exit fullscreen modeThis made several changes, but the most important one is that you now need to specify the language in your
</code> tags as <code>ts</code>. Take a look at the change in <code>src/App.svelte</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Make this change to your Inaction Card component.</p> <h3> <a name="dry-up-inactions" href="#dry-up-inactions" class="anchor"> </a> DRY Up Inactions </h3> <p>To make this adjustment process smoother, let's <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY up</a> our code. Adjust your <code>App.svelte</code> to loop through an array of inactions that we create in the <code><script></code> section.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">inactions</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Return VHS to Blockbuster</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">1992-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">You've had it since 1998. What's the rush?</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Fix the hole in the wall</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Keeping it where it is adds character and nostalgia.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Do the dishes</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">2024-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Someone else will probably get to them. Just leave them in the sink.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">];</span> <span class="nt">
To Don't List
class="intro">Here is a list of things I'm not going to do: {#each inactions as inaction} title={inaction.title} dueDate={inaction.dueDate} notes={inaction.notes} /> {/each}Enter fullscreen mode Exit fullscreen modeDefine Inaction Type
TypeScript is really all about types. (It's in the name, after all!) To add types in a Svelte project, we'll want a separate
.ts
file. Let's put our types in a types directory, just to stay organized.
// src/types/Inaction.ts export interface Inaction { title: string; dueDate: Date; notes: string; }
Enter fullscreen mode Exit fullscreen modeThis tells us that when we set an object as an
Inaction
, we expect it to havetitle
andnotes
as astring
, anddueDate
as aDate
.To use it, import the type and then note it. The syntax is a little goofy at first, but you'll get the hang of it.
<span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="nx">type</span> <span class="p">{</span> <span class="nx">Inaction</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./types/Inaction</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// inactions should be an array of items with the Inaction type.</span> <span class="kd">let</span> <span class="nx">inactions</span><span class="p">:</span> <span class="nx">Inaction</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Return VHS to Blockbuster</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">1992-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">You've had it since 1998. What's the rush?</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Fix the hole in the wall</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Keeping it where it is adds character and nostalgia.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Do the dishes</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">2024-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Someone else will probably get to them. Just leave them in the sink.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">];</span> <span class="nt">
Enter fullscreen mode Exit fullscreen modeAfter I restarted my code editor (VS Code), it told me something was wrong!
This says
dueDate
on the second inaction is the wrong type!The power in this is great! VS Code told me there's an issue with the code without having to open up the browser!
If that's useful even on this small scale, imagine how helpful it would be in a large application where
Inaction
may be used dozens or hundreds of times!Validating Types
Aside from the code editor adding little squiggles, nothing would actually appear wrong during the Svelte build. Fortunately, Svelte gives us a tool to make sure everything is okay,
svelte-check
.Try running the following command:
$ npx svelte-check
Enter fullscreen mode Exit fullscreen modeI see an error and three warnings:
Loading svelte-check in workspace: .../to-dont-list Getting Svelte diagnostics... ==================================== .../to-dont-list/src/App.svelte:13:7 Error: Type 'number' is not assignable to type 'Date'. (ts) title: "Fix the hole in the wall", dueDate: Date.now(), notes: "Keeping it where it is adds character and nostalgia.", .../to-dont-list/src/components/InactionCard.svelte:2:14 Hint: Variable 'title' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate; .../to-dont-list/src/components/InactionCard.svelte:2:21 Hint: Variable 'notes' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate; .../to-dont-list/src/components/InactionCard.svelte:2:28 Hint: Variable 'dueDate' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate;
Enter fullscreen mode Exit fullscreen modeYou can wire up this command to your build process so you don't send invalid typed code to production. Amazing!
I hope this validates (😉) the use of TypeScript and Svelte enough to entice you to try it out for yourself! But before we go, let's take a look at wiring this up to Sanity so that we can pull in data dynamically from an external source.
Wiring up Sanity to a Svelte project
To get Sanity up and running, begin by installing the CLI and bootstrapping a new project.
$ npm install -g @sanity/cli $ sanity init
Enter fullscreen mode Exit fullscreen modeAfter authenticating, choose the appropriate items to get your project started. These were my choices:
- Select project to use: Create a new project
- Your project name: To Not Do
- Use the default dataset configuration? Yes
- Project output path: (Use recommendation)
- Select project template: Clean project with no predefined schemas
That will install dependencies and prepare a Studio project. You should now be able to change into the new directory Sanity created for you and start the Studio.
$ cd path/to/new/directory $ npm start
Enter fullscreen mode Exit fullscreen modeBy default, the Studio will be available in your browser at http://localhost:3333/. You'll have to sign in again. And when you do, you should see that you have an empty schema.
Add Data Model
Let's create our data model, which Sanity calls a document type. If you're not familiar with Sanity, here's a nice overview of content modeling.
To add our model, we're going to edit the (nearly) blank schema file Sanity gave us. This file lives in
schemas/schema.js
. It should look something like this:
// First, we must import the schema creator import createSchema from "part:@sanity/base/schema-creator"; // Then import schema types from any plugins that might expose them import schemaTypes from "all:part:@sanity/base/schema-type"; // Then we give our schema to the builder and provide the result to Sanity export default createSchema({ // We name our schema name: "default", // Then proceed to concatenate our document type // to the ones provided by any plugins that are installed types: schemaTypes.concat([ /* Your types here! */ ]), });
Enter fullscreen mode Exit fullscreen modeWe're going to put our type in the
types
object. Let's create aninaction
model with the proper fields:
import createSchema from "part:@sanity/base/schema-creator"; import schemaTypes from "all:part:@sanity/base/schema-type"; export default createSchema({ name: "default", types: schemaTypes.concat([ { title: "Inaction", name: "inaction", type: "document", fields: [ { title: "Title", name: "title", type: "string" }, { title: "Notes", name: "notes", type: "text" }, { title: "Due Date", name: "dueDate", type: "date" }, ], }, ]), });
Enter fullscreen mode Exit fullscreen modeNotice that if you go back to your browser, Studio already picked up your changes! (Science! Or something?)
Use your new model to recreate the static content from
src/App.svelte
.Pull in Data Dynamically
Once that content is in place, we can pull it into project. First, install Sanity's JavaScript client.
$ npm install @sanity/client
Enter fullscreen mode Exit fullscreen modeThen we can make some changes to the
</code> in our <code>App.svelte</code> component to fetch our inactions from the Sanity API.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="p">{</span> <span class="nx">onMount</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">svelte</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">sanityClient</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@sanity/client</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">type</span> <span class="p">{</span> <span class="nx">Inaction</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./types/Inaction</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// Create a client to connect to the Sanity datastore.</span> <span class="kd">const</span> <span class="nx">sanity</span> <span class="o">=</span> <span class="nx">sanityClient</span><span class="p">({</span> <span class="na">apiVersion</span><span class="p">:</span> <span class="dl">'</span><span class="s1">v2021-03-25</span><span class="dl">'</span><span class="p">,</span> <span class="na">projectId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataset</span><span class="p">:</span> <span class="dl">"</span><span class="s2">production</span><span class="dl">"</span><span class="p">,</span> <span class="na">useCdn</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="p">});</span> <span class="c1">// Initialize our inactions as an empty array.</span> <span class="kd">let</span> <span class="nx">inactions</span><span class="p">:</span> <span class="nx">Inaction</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// Fetch the inactions from Sanity, and replace the array.</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">fetchInactions</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">query</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">*[_type == "inaction"]{ _id, title, notes, dueDate }</span><span class="dl">'</span><span class="p">;</span> <span class="nx">inactions</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">sanity</span><span class="p">.</span><span class="nx">fetch</span><span class="p">(</span><span class="nx">query</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Run the fetch function when the component is ready (mounted).</span> <span class="nx">onMount</span><span class="p">(</span><span class="nx">fetchInactions</span><span class="p">);</span> <span class="nt">
Enter fullscreen mode Exit fullscreen modeAnd now you are reading dynamic data for the list of things you're never going to have to do!
Next Steps
I hope you are intrigued enough by this to to tinker with these super cool tools! And if you want to keep going with this example, here are some ideas on where to go next:
- Add a form to submit new Inaction objects to Sanity. (The demo and its code have a working example.) Note: If you go this route, you'll want to start thinking about authenticating your API requests.
- Deploy the project to a build and hosting service, like Vercel.
- Deploy Sanity Studio so you can make edits in Sanity, without having to run the Studio locally.
I'd love to learn where you choose to take your project. The things we can build with Svelte, TypeScript, and Sanity are endless! Let's chat!
Last, here are a few other resources for further reading on Svelte + TypeScript:
- Svelte and TypeScript - Together at last! (Scott Logic)
- TypeScript support in Svelte (MDN)
- Integrating TypeScript with Svelte (CSS-Tricks)
- Svelte <3 TypeScript (Svelte)
-
TypeScript is an extension of JavaScript. You can think of it as JavaScript with a few extra features. These features are largely focused on defining the type and shape of JavaScript objects. It requires that you be declarative about the code you're writing and have an understanding of the values your functions, variables, and objects are expecting.
-
SurveyJS
Open-Source JSON Form Builder to Create Dynamic Forms Right in Your App. With SurveyJS form UI libraries, you can build and style forms in a fully-integrated drag & drop form builder, render them in your JS app, and store form submission data in any backend, inc. PHP, ASP.NET Core, and Node.js.
-
Note: If you want to follow along with styling, you can replace public/globals.css with the full example code, and also pull contents from individual component, like this one.
Render (Static) Inaction Cards
To bring it to life on the home page, adjust the default intro copy and render a few inactions from the main
src/App.svelte
component.
</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="nt">
To Don't List
class="intro">Here is a list of things I'm not going to do: title="Return VHS to Blockbuster" notes="You've had it since 1998. What's the rush?" /> title="Fix the hole in the wall" notes="Keeping it where it is adds character and nostalgia." /> title="Do the dishes" notes="Someone else will probably get to them. Just leave them in the sink." />Enter fullscreen mode Exit fullscreen modeTake a look back at your browser and you should see the list! (Refresh if you don't.)
There we go! We have a (very simple) Svelte app with just HTML, CSS, and JavaScript.
Adding TypeScript to a Svelte project
Now, suppose we wanted to add a due date. It's not too difficult in this example, right? Let's do it!
We add a new property to the Inaction Card component:
</span> <span class="k">export</span> <span class="kd">let</span> <span class="nx">title</span><span class="p">,</span> <span class="nx">notes</span><span class="p">,</span> <span class="nx">dueDate</span><span class="p">;</span> <span class="nt"> class="inaction"> class="title">{title} class="due-date">{dueDate} class="notes">{notes}
Enter fullscreen mode Exit fullscreen modeAnd to the inactions we render, we can add the new prop:
title="Return VHS to Blockbuster" dueDate={new Date("1992-02-29")} notes="You've had it since 1998. What's the rush?" /> title="Fix the hole in the wall" dueDate={Date.now()} notes="Keeping it where it is adds character and nostalgia." /> title="Do the dishes" dueDate={new Date("2024-02-29")} notes="Someone else will probably get to them. Just leave them in the sink." />
Enter fullscreen mode Exit fullscreen modeThen jump back over to your browser:
Notice anything weird? I do. Two things, actually:
- The first and the third inactions display an unnecessarily-long date. That could have been easily formatted, but we're not going to worry about that here.
- The second date is a number. That's a bigger issue, and I'll explain why.
What if, we rendered the type of
dueDate
instead? (i.e.typeof dueDate
)We get
number
for the second one! Even with this small of an application that could absolutely be an issue. Suppose you wanted to better control the formatting of the date. You'd have to first introspect the type of date and then format appropriately. It'd be much easier if we know (with confidence) what type to expect.TypeScript Saves the Day!
We can use TypeScript to tell our application exactly what we expect in
dueDate
.Since our Svelte app is still largely untouched, we can use the built-in utility to convert it to TypeScript. Shut down your development server (
ctrl
+c
), and then run the following commands:
$ node scripts/setupTypeScript.js $ npm install
Enter fullscreen mode Exit fullscreen modeThis made several changes, but the most important one is that you now need to specify the language in your
</code> tags as <code>ts</code>. Take a look at the change in <code>src/App.svelte</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Make this change to your Inaction Card component.</p> <h3> <a name="dry-up-inactions" href="#dry-up-inactions" class="anchor"> </a> DRY Up Inactions </h3> <p>To make this adjustment process smoother, let's <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY up</a> our code. Adjust your <code>App.svelte</code> to loop through an array of inactions that we create in the <code><script></code> section.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">inactions</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Return VHS to Blockbuster</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">1992-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">You've had it since 1998. What's the rush?</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Fix the hole in the wall</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Keeping it where it is adds character and nostalgia.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Do the dishes</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">2024-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Someone else will probably get to them. Just leave them in the sink.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">];</span> <span class="nt">
To Don't List
class="intro">Here is a list of things I'm not going to do: {#each inactions as inaction} title={inaction.title} dueDate={inaction.dueDate} notes={inaction.notes} /> {/each}Enter fullscreen mode Exit fullscreen modeDefine Inaction Type
TypeScript is really all about types. (It's in the name, after all!) To add types in a Svelte project, we'll want a separate
.ts
file. Let's put our types in a types directory, just to stay organized.
// src/types/Inaction.ts export interface Inaction { title: string; dueDate: Date; notes: string; }
Enter fullscreen mode Exit fullscreen modeThis tells us that when we set an object as an
Inaction
, we expect it to havetitle
andnotes
as astring
, anddueDate
as aDate
.To use it, import the type and then note it. The syntax is a little goofy at first, but you'll get the hang of it.
<span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="nx">type</span> <span class="p">{</span> <span class="nx">Inaction</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./types/Inaction</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// inactions should be an array of items with the Inaction type.</span> <span class="kd">let</span> <span class="nx">inactions</span><span class="p">:</span> <span class="nx">Inaction</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Return VHS to Blockbuster</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">1992-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">You've had it since 1998. What's the rush?</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Fix the hole in the wall</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Keeping it where it is adds character and nostalgia.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Do the dishes</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">2024-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Someone else will probably get to them. Just leave them in the sink.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">];</span> <span class="nt">
Enter fullscreen mode Exit fullscreen modeAfter I restarted my code editor (VS Code), it told me something was wrong!
This says
dueDate
on the second inaction is the wrong type!The power in this is great! VS Code told me there's an issue with the code without having to open up the browser!
If that's useful even on this small scale, imagine how helpful it would be in a large application where
Inaction
may be used dozens or hundreds of times!Validating Types
Aside from the code editor adding little squiggles, nothing would actually appear wrong during the Svelte build. Fortunately, Svelte gives us a tool to make sure everything is okay,
svelte-check
.Try running the following command:
$ npx svelte-check
Enter fullscreen mode Exit fullscreen modeI see an error and three warnings:
Loading svelte-check in workspace: .../to-dont-list Getting Svelte diagnostics... ==================================== .../to-dont-list/src/App.svelte:13:7 Error: Type 'number' is not assignable to type 'Date'. (ts) title: "Fix the hole in the wall", dueDate: Date.now(), notes: "Keeping it where it is adds character and nostalgia.", .../to-dont-list/src/components/InactionCard.svelte:2:14 Hint: Variable 'title' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate; .../to-dont-list/src/components/InactionCard.svelte:2:21 Hint: Variable 'notes' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate; .../to-dont-list/src/components/InactionCard.svelte:2:28 Hint: Variable 'dueDate' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate;
Enter fullscreen mode Exit fullscreen modeYou can wire up this command to your build process so you don't send invalid typed code to production. Amazing!
I hope this validates (😉) the use of TypeScript and Svelte enough to entice you to try it out for yourself! But before we go, let's take a look at wiring this up to Sanity so that we can pull in data dynamically from an external source.
Wiring up Sanity to a Svelte project
To get Sanity up and running, begin by installing the CLI and bootstrapping a new project.
$ npm install -g @sanity/cli $ sanity init
Enter fullscreen mode Exit fullscreen modeAfter authenticating, choose the appropriate items to get your project started. These were my choices:
- Select project to use: Create a new project
- Your project name: To Not Do
- Use the default dataset configuration? Yes
- Project output path: (Use recommendation)
- Select project template: Clean project with no predefined schemas
That will install dependencies and prepare a Studio project. You should now be able to change into the new directory Sanity created for you and start the Studio.
$ cd path/to/new/directory $ npm start
Enter fullscreen mode Exit fullscreen modeBy default, the Studio will be available in your browser at http://localhost:3333/. You'll have to sign in again. And when you do, you should see that you have an empty schema.
Add Data Model
Let's create our data model, which Sanity calls a document type. If you're not familiar with Sanity, here's a nice overview of content modeling.
To add our model, we're going to edit the (nearly) blank schema file Sanity gave us. This file lives in
schemas/schema.js
. It should look something like this:
// First, we must import the schema creator import createSchema from "part:@sanity/base/schema-creator"; // Then import schema types from any plugins that might expose them import schemaTypes from "all:part:@sanity/base/schema-type"; // Then we give our schema to the builder and provide the result to Sanity export default createSchema({ // We name our schema name: "default", // Then proceed to concatenate our document type // to the ones provided by any plugins that are installed types: schemaTypes.concat([ /* Your types here! */ ]), });
Enter fullscreen mode Exit fullscreen modeWe're going to put our type in the
types
object. Let's create aninaction
model with the proper fields:
import createSchema from "part:@sanity/base/schema-creator"; import schemaTypes from "all:part:@sanity/base/schema-type"; export default createSchema({ name: "default", types: schemaTypes.concat([ { title: "Inaction", name: "inaction", type: "document", fields: [ { title: "Title", name: "title", type: "string" }, { title: "Notes", name: "notes", type: "text" }, { title: "Due Date", name: "dueDate", type: "date" }, ], }, ]), });
Enter fullscreen mode Exit fullscreen modeNotice that if you go back to your browser, Studio already picked up your changes! (Science! Or something?)
Use your new model to recreate the static content from
src/App.svelte
.Pull in Data Dynamically
Once that content is in place, we can pull it into project. First, install Sanity's JavaScript client.
$ npm install @sanity/client
Enter fullscreen mode Exit fullscreen modeThen we can make some changes to the
</code> in our <code>App.svelte</code> component to fetch our inactions from the Sanity API.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="p">{</span> <span class="nx">onMount</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">svelte</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">sanityClient</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@sanity/client</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">type</span> <span class="p">{</span> <span class="nx">Inaction</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./types/Inaction</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// Create a client to connect to the Sanity datastore.</span> <span class="kd">const</span> <span class="nx">sanity</span> <span class="o">=</span> <span class="nx">sanityClient</span><span class="p">({</span> <span class="na">apiVersion</span><span class="p">:</span> <span class="dl">'</span><span class="s1">v2021-03-25</span><span class="dl">'</span><span class="p">,</span> <span class="na">projectId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataset</span><span class="p">:</span> <span class="dl">"</span><span class="s2">production</span><span class="dl">"</span><span class="p">,</span> <span class="na">useCdn</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="p">});</span> <span class="c1">// Initialize our inactions as an empty array.</span> <span class="kd">let</span> <span class="nx">inactions</span><span class="p">:</span> <span class="nx">Inaction</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// Fetch the inactions from Sanity, and replace the array.</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">fetchInactions</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">query</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">*[_type == "inaction"]{ _id, title, notes, dueDate }</span><span class="dl">'</span><span class="p">;</span> <span class="nx">inactions</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">sanity</span><span class="p">.</span><span class="nx">fetch</span><span class="p">(</span><span class="nx">query</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Run the fetch function when the component is ready (mounted).</span> <span class="nx">onMount</span><span class="p">(</span><span class="nx">fetchInactions</span><span class="p">);</span> <span class="nt">
Enter fullscreen mode Exit fullscreen modeAnd now you are reading dynamic data for the list of things you're never going to have to do!
Next Steps
I hope you are intrigued enough by this to to tinker with these super cool tools! And if you want to keep going with this example, here are some ideas on where to go next:
- Add a form to submit new Inaction objects to Sanity. (The demo and its code have a working example.) Note: If you go this route, you'll want to start thinking about authenticating your API requests.
- Deploy the project to a build and hosting service, like Vercel.
- Deploy Sanity Studio so you can make edits in Sanity, without having to run the Studio locally.
I'd love to learn where you choose to take your project. The things we can build with Svelte, TypeScript, and Sanity are endless! Let's chat!
Last, here are a few other resources for further reading on Svelte + TypeScript:
- Svelte and TypeScript - Together at last! (Scott Logic)
- TypeScript support in Svelte (MDN)
- Integrating TypeScript with Svelte (CSS-Tricks)
- Svelte <3 TypeScript (Svelte)
-
Note: If you want to follow along with styling, you can replace public/globals.css with the full example code, and also pull contents from individual component, like this one.
Render (Static) Inaction Cards
To bring it to life on the home page, adjust the default intro copy and render a few inactions from the main
src/App.svelte
component.
</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="nt">
To Don't List
class="intro">Here is a list of things I'm not going to do: title="Return VHS to Blockbuster" notes="You've had it since 1998. What's the rush?" /> title="Fix the hole in the wall" notes="Keeping it where it is adds character and nostalgia." /> title="Do the dishes" notes="Someone else will probably get to them. Just leave them in the sink." />Enter fullscreen mode Exit fullscreen modeTake a look back at your browser and you should see the list! (Refresh if you don't.)
There we go! We have a (very simple) Svelte app with just HTML, CSS, and JavaScript.
Adding TypeScript to a Svelte project
Now, suppose we wanted to add a due date. It's not too difficult in this example, right? Let's do it!
We add a new property to the Inaction Card component:
</span> <span class="k">export</span> <span class="kd">let</span> <span class="nx">title</span><span class="p">,</span> <span class="nx">notes</span><span class="p">,</span> <span class="nx">dueDate</span><span class="p">;</span> <span class="nt"> class="inaction"> class="title">{title} class="due-date">{dueDate} class="notes">{notes}
Enter fullscreen mode Exit fullscreen modeAnd to the inactions we render, we can add the new prop:
title="Return VHS to Blockbuster" dueDate={new Date("1992-02-29")} notes="You've had it since 1998. What's the rush?" /> title="Fix the hole in the wall" dueDate={Date.now()} notes="Keeping it where it is adds character and nostalgia." /> title="Do the dishes" dueDate={new Date("2024-02-29")} notes="Someone else will probably get to them. Just leave them in the sink." />
Enter fullscreen mode Exit fullscreen modeThen jump back over to your browser:
Notice anything weird? I do. Two things, actually:
- The first and the third inactions display an unnecessarily-long date. That could have been easily formatted, but we're not going to worry about that here.
- The second date is a number. That's a bigger issue, and I'll explain why.
What if, we rendered the type of
dueDate
instead? (i.e.typeof dueDate
)We get
number
for the second one! Even with this small of an application that could absolutely be an issue. Suppose you wanted to better control the formatting of the date. You'd have to first introspect the type of date and then format appropriately. It'd be much easier if we know (with confidence) what type to expect.TypeScript Saves the Day!
We can use TypeScript to tell our application exactly what we expect in
dueDate
.Since our Svelte app is still largely untouched, we can use the built-in utility to convert it to TypeScript. Shut down your development server (
ctrl
+c
), and then run the following commands:
$ node scripts/setupTypeScript.js $ npm install
Enter fullscreen mode Exit fullscreen modeThis made several changes, but the most important one is that you now need to specify the language in your
</code> tags as <code>ts</code>. Take a look at the change in <code>src/App.svelte</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Make this change to your Inaction Card component.</p> <h3> <a name="dry-up-inactions" href="#dry-up-inactions" class="anchor"> </a> DRY Up Inactions </h3> <p>To make this adjustment process smoother, let's <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY up</a> our code. Adjust your <code>App.svelte</code> to loop through an array of inactions that we create in the <code><script></code> section.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">inactions</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Return VHS to Blockbuster</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">1992-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">You've had it since 1998. What's the rush?</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Fix the hole in the wall</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Keeping it where it is adds character and nostalgia.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Do the dishes</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">2024-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Someone else will probably get to them. Just leave them in the sink.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">];</span> <span class="nt">
To Don't List
class="intro">Here is a list of things I'm not going to do: {#each inactions as inaction} title={inaction.title} dueDate={inaction.dueDate} notes={inaction.notes} /> {/each}Enter fullscreen mode Exit fullscreen modeDefine Inaction Type
TypeScript is really all about types. (It's in the name, after all!) To add types in a Svelte project, we'll want a separate
.ts
file. Let's put our types in a types directory, just to stay organized.
// src/types/Inaction.ts export interface Inaction { title: string; dueDate: Date; notes: string; }
Enter fullscreen mode Exit fullscreen modeThis tells us that when we set an object as an
Inaction
, we expect it to havetitle
andnotes
as astring
, anddueDate
as aDate
.To use it, import the type and then note it. The syntax is a little goofy at first, but you'll get the hang of it.
<span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="nx">type</span> <span class="p">{</span> <span class="nx">Inaction</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./types/Inaction</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// inactions should be an array of items with the Inaction type.</span> <span class="kd">let</span> <span class="nx">inactions</span><span class="p">:</span> <span class="nx">Inaction</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Return VHS to Blockbuster</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">1992-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">You've had it since 1998. What's the rush?</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Fix the hole in the wall</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Keeping it where it is adds character and nostalgia.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Do the dishes</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">2024-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Someone else will probably get to them. Just leave them in the sink.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">];</span> <span class="nt">
Enter fullscreen mode Exit fullscreen modeAfter I restarted my code editor (VS Code), it told me something was wrong!
This says
dueDate
on the second inaction is the wrong type!The power in this is great! VS Code told me there's an issue with the code without having to open up the browser!
If that's useful even on this small scale, imagine how helpful it would be in a large application where
Inaction
may be used dozens or hundreds of times!Validating Types
Aside from the code editor adding little squiggles, nothing would actually appear wrong during the Svelte build. Fortunately, Svelte gives us a tool to make sure everything is okay,
svelte-check
.Try running the following command:
$ npx svelte-check
Enter fullscreen mode Exit fullscreen modeI see an error and three warnings:
Loading svelte-check in workspace: .../to-dont-list Getting Svelte diagnostics... ==================================== .../to-dont-list/src/App.svelte:13:7 Error: Type 'number' is not assignable to type 'Date'. (ts) title: "Fix the hole in the wall", dueDate: Date.now(), notes: "Keeping it where it is adds character and nostalgia.", .../to-dont-list/src/components/InactionCard.svelte:2:14 Hint: Variable 'title' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate; .../to-dont-list/src/components/InactionCard.svelte:2:21 Hint: Variable 'notes' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate; .../to-dont-list/src/components/InactionCard.svelte:2:28 Hint: Variable 'dueDate' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate;
Enter fullscreen mode Exit fullscreen modeYou can wire up this command to your build process so you don't send invalid typed code to production. Amazing!
I hope this validates (😉) the use of TypeScript and Svelte enough to entice you to try it out for yourself! But before we go, let's take a look at wiring this up to Sanity so that we can pull in data dynamically from an external source.
Wiring up Sanity to a Svelte project
To get Sanity up and running, begin by installing the CLI and bootstrapping a new project.
$ npm install -g @sanity/cli $ sanity init
Enter fullscreen mode Exit fullscreen modeAfter authenticating, choose the appropriate items to get your project started. These were my choices:
- Select project to use: Create a new project
- Your project name: To Not Do
- Use the default dataset configuration? Yes
- Project output path: (Use recommendation)
- Select project template: Clean project with no predefined schemas
That will install dependencies and prepare a Studio project. You should now be able to change into the new directory Sanity created for you and start the Studio.
$ cd path/to/new/directory $ npm start
Enter fullscreen mode Exit fullscreen modeBy default, the Studio will be available in your browser at http://localhost:3333/. You'll have to sign in again. And when you do, you should see that you have an empty schema.
Add Data Model
Let's create our data model, which Sanity calls a document type. If you're not familiar with Sanity, here's a nice overview of content modeling.
To add our model, we're going to edit the (nearly) blank schema file Sanity gave us. This file lives in
schemas/schema.js
. It should look something like this:
// First, we must import the schema creator import createSchema from "part:@sanity/base/schema-creator"; // Then import schema types from any plugins that might expose them import schemaTypes from "all:part:@sanity/base/schema-type"; // Then we give our schema to the builder and provide the result to Sanity export default createSchema({ // We name our schema name: "default", // Then proceed to concatenate our document type // to the ones provided by any plugins that are installed types: schemaTypes.concat([ /* Your types here! */ ]), });
Enter fullscreen mode Exit fullscreen modeWe're going to put our type in the
types
object. Let's create aninaction
model with the proper fields:
import createSchema from "part:@sanity/base/schema-creator"; import schemaTypes from "all:part:@sanity/base/schema-type"; export default createSchema({ name: "default", types: schemaTypes.concat([ { title: "Inaction", name: "inaction", type: "document", fields: [ { title: "Title", name: "title", type: "string" }, { title: "Notes", name: "notes", type: "text" }, { title: "Due Date", name: "dueDate", type: "date" }, ], }, ]), });
Enter fullscreen mode Exit fullscreen modeNotice that if you go back to your browser, Studio already picked up your changes! (Science! Or something?)
Use your new model to recreate the static content from
src/App.svelte
.Pull in Data Dynamically
Once that content is in place, we can pull it into project. First, install Sanity's JavaScript client.
$ npm install @sanity/client
Enter fullscreen mode Exit fullscreen modeThen we can make some changes to the
</code> in our <code>App.svelte</code> component to fetch our inactions from the Sanity API.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="p">{</span> <span class="nx">onMount</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">svelte</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">sanityClient</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@sanity/client</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">type</span> <span class="p">{</span> <span class="nx">Inaction</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./types/Inaction</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// Create a client to connect to the Sanity datastore.</span> <span class="kd">const</span> <span class="nx">sanity</span> <span class="o">=</span> <span class="nx">sanityClient</span><span class="p">({</span> <span class="na">apiVersion</span><span class="p">:</span> <span class="dl">'</span><span class="s1">v2021-03-25</span><span class="dl">'</span><span class="p">,</span> <span class="na">projectId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataset</span><span class="p">:</span> <span class="dl">"</span><span class="s2">production</span><span class="dl">"</span><span class="p">,</span> <span class="na">useCdn</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="p">});</span> <span class="c1">// Initialize our inactions as an empty array.</span> <span class="kd">let</span> <span class="nx">inactions</span><span class="p">:</span> <span class="nx">Inaction</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// Fetch the inactions from Sanity, and replace the array.</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">fetchInactions</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">query</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">*[_type == "inaction"]{ _id, title, notes, dueDate }</span><span class="dl">'</span><span class="p">;</span> <span class="nx">inactions</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">sanity</span><span class="p">.</span><span class="nx">fetch</span><span class="p">(</span><span class="nx">query</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Run the fetch function when the component is ready (mounted).</span> <span class="nx">onMount</span><span class="p">(</span><span class="nx">fetchInactions</span><span class="p">);</span> <span class="nt">
Enter fullscreen mode Exit fullscreen modeAnd now you are reading dynamic data for the list of things you're never going to have to do!
Next Steps
I hope you are intrigued enough by this to to tinker with these super cool tools! And if you want to keep going with this example, here are some ideas on where to go next:
- Add a form to submit new Inaction objects to Sanity. (The demo and its code have a working example.) Note: If you go this route, you'll want to start thinking about authenticating your API requests.
- Deploy the project to a build and hosting service, like Vercel.
- Deploy Sanity Studio so you can make edits in Sanity, without having to run the Studio locally.
I'd love to learn where you choose to take your project. The things we can build with Svelte, TypeScript, and Sanity are endless! Let's chat!
Last, here are a few other resources for further reading on Svelte + TypeScript:
- Svelte and TypeScript - Together at last! (Scott Logic)
- TypeScript support in Svelte (MDN)
- Integrating TypeScript with Svelte (CSS-Tricks)
- Svelte <3 TypeScript (Svelte)
-
Hear me out for a moment. It has its place. It's like React in that it is a component-centered means of building out UIs. In fact, Svelte often uses React in explaining its theoretical approach. There are nuances that separate the two, with pros and cons to each. But, to me, it boils down to one main idea when differentiating Svelte from React:
-
For that reason, I really like Svelte for simple use cases. But, with wider adoption and tools like Next.js, React is still my go-to for larger scale or more complex application interfaces.
-
Note: If you want to follow along with styling, you can replace public/globals.css with the full example code, and also pull contents from individual component, like this one.
Render (Static) Inaction Cards
To bring it to life on the home page, adjust the default intro copy and render a few inactions from the main
src/App.svelte
component.
</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="nt">
To Don't List
class="intro">Here is a list of things I'm not going to do: title="Return VHS to Blockbuster" notes="You've had it since 1998. What's the rush?" /> title="Fix the hole in the wall" notes="Keeping it where it is adds character and nostalgia." /> title="Do the dishes" notes="Someone else will probably get to them. Just leave them in the sink." />Enter fullscreen mode Exit fullscreen modeTake a look back at your browser and you should see the list! (Refresh if you don't.)
There we go! We have a (very simple) Svelte app with just HTML, CSS, and JavaScript.
Adding TypeScript to a Svelte project
Now, suppose we wanted to add a due date. It's not too difficult in this example, right? Let's do it!
We add a new property to the Inaction Card component:
</span> <span class="k">export</span> <span class="kd">let</span> <span class="nx">title</span><span class="p">,</span> <span class="nx">notes</span><span class="p">,</span> <span class="nx">dueDate</span><span class="p">;</span> <span class="nt"> class="inaction"> class="title">{title} class="due-date">{dueDate} class="notes">{notes}
Enter fullscreen mode Exit fullscreen modeAnd to the inactions we render, we can add the new prop:
title="Return VHS to Blockbuster" dueDate={new Date("1992-02-29")} notes="You've had it since 1998. What's the rush?" /> title="Fix the hole in the wall" dueDate={Date.now()} notes="Keeping it where it is adds character and nostalgia." /> title="Do the dishes" dueDate={new Date("2024-02-29")} notes="Someone else will probably get to them. Just leave them in the sink." />
Enter fullscreen mode Exit fullscreen modeThen jump back over to your browser:
Notice anything weird? I do. Two things, actually:
- The first and the third inactions display an unnecessarily-long date. That could have been easily formatted, but we're not going to worry about that here.
- The second date is a number. That's a bigger issue, and I'll explain why.
What if, we rendered the type of
dueDate
instead? (i.e.typeof dueDate
)We get
number
for the second one! Even with this small of an application that could absolutely be an issue. Suppose you wanted to better control the formatting of the date. You'd have to first introspect the type of date and then format appropriately. It'd be much easier if we know (with confidence) what type to expect.TypeScript Saves the Day!
We can use TypeScript to tell our application exactly what we expect in
dueDate
.Since our Svelte app is still largely untouched, we can use the built-in utility to convert it to TypeScript. Shut down your development server (
ctrl
+c
), and then run the following commands:
$ node scripts/setupTypeScript.js $ npm install
Enter fullscreen mode Exit fullscreen modeThis made several changes, but the most important one is that you now need to specify the language in your
</code> tags as <code>ts</code>. Take a look at the change in <code>src/App.svelte</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Make this change to your Inaction Card component.</p> <h3> <a name="dry-up-inactions" href="#dry-up-inactions" class="anchor"> </a> DRY Up Inactions </h3> <p>To make this adjustment process smoother, let's <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY up</a> our code. Adjust your <code>App.svelte</code> to loop through an array of inactions that we create in the <code><script></code> section.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">inactions</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Return VHS to Blockbuster</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">1992-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">You've had it since 1998. What's the rush?</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Fix the hole in the wall</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Keeping it where it is adds character and nostalgia.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Do the dishes</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">2024-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Someone else will probably get to them. Just leave them in the sink.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">];</span> <span class="nt">
To Don't List
class="intro">Here is a list of things I'm not going to do: {#each inactions as inaction} title={inaction.title} dueDate={inaction.dueDate} notes={inaction.notes} /> {/each}Enter fullscreen mode Exit fullscreen modeDefine Inaction Type
TypeScript is really all about types. (It's in the name, after all!) To add types in a Svelte project, we'll want a separate
.ts
file. Let's put our types in a types directory, just to stay organized.
// src/types/Inaction.ts export interface Inaction { title: string; dueDate: Date; notes: string; }
Enter fullscreen mode Exit fullscreen modeThis tells us that when we set an object as an
Inaction
, we expect it to havetitle
andnotes
as astring
, anddueDate
as aDate
.To use it, import the type and then note it. The syntax is a little goofy at first, but you'll get the hang of it.
<span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="nx">type</span> <span class="p">{</span> <span class="nx">Inaction</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./types/Inaction</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// inactions should be an array of items with the Inaction type.</span> <span class="kd">let</span> <span class="nx">inactions</span><span class="p">:</span> <span class="nx">Inaction</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Return VHS to Blockbuster</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">1992-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">You've had it since 1998. What's the rush?</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Fix the hole in the wall</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">(),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Keeping it where it is adds character and nostalgia.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">{</span> <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Do the dishes</span><span class="dl">"</span><span class="p">,</span> <span class="na">dueDate</span><span class="p">:</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">(</span><span class="dl">"</span><span class="s2">2024-02-29</span><span class="dl">"</span><span class="p">),</span> <span class="na">notes</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Someone else will probably get to them. Just leave them in the sink.</span><span class="dl">"</span><span class="p">,</span> <span class="p">},</span> <span class="p">];</span> <span class="nt">
Enter fullscreen mode Exit fullscreen modeAfter I restarted my code editor (VS Code), it told me something was wrong!
This says
dueDate
on the second inaction is the wrong type!The power in this is great! VS Code told me there's an issue with the code without having to open up the browser!
If that's useful even on this small scale, imagine how helpful it would be in a large application where
Inaction
may be used dozens or hundreds of times!Validating Types
Aside from the code editor adding little squiggles, nothing would actually appear wrong during the Svelte build. Fortunately, Svelte gives us a tool to make sure everything is okay,
svelte-check
.Try running the following command:
$ npx svelte-check
Enter fullscreen mode Exit fullscreen modeI see an error and three warnings:
Loading svelte-check in workspace: .../to-dont-list Getting Svelte diagnostics... ==================================== .../to-dont-list/src/App.svelte:13:7 Error: Type 'number' is not assignable to type 'Date'. (ts) title: "Fix the hole in the wall", dueDate: Date.now(), notes: "Keeping it where it is adds character and nostalgia.", .../to-dont-list/src/components/InactionCard.svelte:2:14 Hint: Variable 'title' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate; .../to-dont-list/src/components/InactionCard.svelte:2:21 Hint: Variable 'notes' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate; .../to-dont-list/src/components/InactionCard.svelte:2:28 Hint: Variable 'dueDate' implicitly has an 'any' type, but a better type may be inferred from usage. (ts) export let title, notes, dueDate;
Enter fullscreen mode Exit fullscreen modeYou can wire up this command to your build process so you don't send invalid typed code to production. Amazing!
I hope this validates (😉) the use of TypeScript and Svelte enough to entice you to try it out for yourself! But before we go, let's take a look at wiring this up to Sanity so that we can pull in data dynamically from an external source.
Wiring up Sanity to a Svelte project
To get Sanity up and running, begin by installing the CLI and bootstrapping a new project.
$ npm install -g @sanity/cli $ sanity init
Enter fullscreen mode Exit fullscreen modeAfter authenticating, choose the appropriate items to get your project started. These were my choices:
- Select project to use: Create a new project
- Your project name: To Not Do
- Use the default dataset configuration? Yes
- Project output path: (Use recommendation)
- Select project template: Clean project with no predefined schemas
That will install dependencies and prepare a Studio project. You should now be able to change into the new directory Sanity created for you and start the Studio.
$ cd path/to/new/directory $ npm start
Enter fullscreen mode Exit fullscreen modeBy default, the Studio will be available in your browser at http://localhost:3333/. You'll have to sign in again. And when you do, you should see that you have an empty schema.
Add Data Model
Let's create our data model, which Sanity calls a document type. If you're not familiar with Sanity, here's a nice overview of content modeling.
To add our model, we're going to edit the (nearly) blank schema file Sanity gave us. This file lives in
schemas/schema.js
. It should look something like this:
// First, we must import the schema creator import createSchema from "part:@sanity/base/schema-creator"; // Then import schema types from any plugins that might expose them import schemaTypes from "all:part:@sanity/base/schema-type"; // Then we give our schema to the builder and provide the result to Sanity export default createSchema({ // We name our schema name: "default", // Then proceed to concatenate our document type // to the ones provided by any plugins that are installed types: schemaTypes.concat([ /* Your types here! */ ]), });
Enter fullscreen mode Exit fullscreen modeWe're going to put our type in the
types
object. Let's create aninaction
model with the proper fields:
import createSchema from "part:@sanity/base/schema-creator"; import schemaTypes from "all:part:@sanity/base/schema-type"; export default createSchema({ name: "default", types: schemaTypes.concat([ { title: "Inaction", name: "inaction", type: "document", fields: [ { title: "Title", name: "title", type: "string" }, { title: "Notes", name: "notes", type: "text" }, { title: "Due Date", name: "dueDate", type: "date" }, ], }, ]), });
Enter fullscreen mode Exit fullscreen modeNotice that if you go back to your browser, Studio already picked up your changes! (Science! Or something?)
Use your new model to recreate the static content from
src/App.svelte
.Pull in Data Dynamically
Once that content is in place, we can pull it into project. First, install Sanity's JavaScript client.
$ npm install @sanity/client
Enter fullscreen mode Exit fullscreen modeThen we can make some changes to the
</code> in our <code>App.svelte</code> component to fetch our inactions from the Sanity API.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="c"><!-- src/App.svelte --></span> <span class="nt"><script </span><span class="na">lang=</span><span class="s">"ts"</span><span class="nt">></span> <span class="k">import</span> <span class="p">{</span> <span class="nx">onMount</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">svelte</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">sanityClient</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@sanity/client</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">type</span> <span class="p">{</span> <span class="nx">Inaction</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./types/Inaction</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">InactionCard</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./components/InactionCard.svelte</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// Create a client to connect to the Sanity datastore.</span> <span class="kd">const</span> <span class="nx">sanity</span> <span class="o">=</span> <span class="nx">sanityClient</span><span class="p">({</span> <span class="na">apiVersion</span><span class="p">:</span> <span class="dl">'</span><span class="s1">v2021-03-25</span><span class="dl">'</span><span class="p">,</span> <span class="na">projectId</span><span class="p">:</span> <span class="dl">"</span><span class="s2">...</span><span class="dl">"</span><span class="p">,</span> <span class="na">dataset</span><span class="p">:</span> <span class="dl">"</span><span class="s2">production</span><span class="dl">"</span><span class="p">,</span> <span class="na">useCdn</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span> <span class="p">});</span> <span class="c1">// Initialize our inactions as an empty array.</span> <span class="kd">let</span> <span class="nx">inactions</span><span class="p">:</span> <span class="nx">Inaction</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// Fetch the inactions from Sanity, and replace the array.</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">fetchInactions</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">query</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">*[_type == "inaction"]{ _id, title, notes, dueDate }</span><span class="dl">'</span><span class="p">;</span> <span class="nx">inactions</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">sanity</span><span class="p">.</span><span class="nx">fetch</span><span class="p">(</span><span class="nx">query</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Run the fetch function when the component is ready (mounted).</span> <span class="nx">onMount</span><span class="p">(</span><span class="nx">fetchInactions</span><span class="p">);</span> <span class="nt">
Enter fullscreen mode Exit fullscreen modeAnd now you are reading dynamic data for the list of things you're never going to have to do!
Next Steps
I hope you are intrigued enough by this to to tinker with these super cool tools! And if you want to keep going with this example, here are some ideas on where to go next:
- Add a form to submit new Inaction objects to Sanity. (The demo and its code have a working example.) Note: If you go this route, you'll want to start thinking about authenticating your API requests.
- Deploy the project to a build and hosting service, like Vercel.
- Deploy Sanity Studio so you can make edits in Sanity, without having to run the Studio locally.
I'd love to learn where you choose to take your project. The things we can build with Svelte, TypeScript, and Sanity are endless! Let's chat!
Last, here are a few other resources for further reading on Svelte + TypeScript:
- Svelte and TypeScript - Together at last! (Scott Logic)
- TypeScript support in Svelte (MDN)
- Integrating TypeScript with Svelte (CSS-Tricks)
- Svelte <3 TypeScript (Svelte)
-
WorkOS
The modern identity platform for B2B SaaS. The APIs are flexible and easy-to-use, supporting authentication, user identity, and complex enterprise features like SSO and SCIM provisioning.