How to Make a Website with Svelte and SvelteKit
Tags: svelte, prismic, tutorial, webdev
Published: 2021-03-24 14:24:44

---
title: How to Make a Website with Svelte and SvelteKit
published: true
description: Svelte is one of the fastest-growing web development frameworks, and Sveltekit is Svelte's web app development framework. In this tutorial, you'll learn how to build and launch a website from scratch with Svelte and Sveltekit.
tags: svelte, prismic, tutorial, webdev
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q0uke6v2hyqx4sauq34u.png
canonical_url: https://prismic.io/blog/svelte-sveltekit-tutorial
---

Svelte is one of the fastest-growing web development frameworks, and Sveltekit is Svelte's web app development framework. In this tutorial, you'll learn how to build and launch a website from scratch with Svelte and Sveltekit.

**But, head's up!** Svelte is in beta, so it still has lots of bugs, and many of these steps are likely to change.

You can see the [final code for this project on GitHub](https://github.com/samlfair/svelte-tutorial), and [the final project live on Netlify](https://lucid-murdock-988f01.netlify.app/).

## What is Svelte?

Svelte is a JavaScript framework for creating web apps. Whereas other frameworks like React and Vue.js generally add code to your web app to make it work in the user's browser, Svelte compiles the code that you write when you build your app. In doing so, it creates very small files and fast websites.

As a compiler, when you write Svelte, it looks unique. Here's an example of a Svelte file:

```html
<script>
  let name = 'world';
</script>
<h1>Hello {name}!</h1>
```

That will generate a web page that looks like this:

![Hello world svelte example in the browser](https://images.prismic.io/wroom/e6ce8b09-3062-41b8-88dc-06ba630deaed_Screen+Shot+2021-03-22+at+2.37.02+PM.png?auto=compress,format)

Svelte looks like HTML, with `<script>` and `<style>` tags included, but it also adds syntax to make your HTML dynamic, inside curly braces. All of this code gets transformed into vanilla HTML, CSS, and JavaScript with Svelte's compiler.

## What is Sveltekit?

Currently the most popular application framework for Svelte is Sapper. Sveltekit is the new official framework developed by Svelte, superseding Sapper as Svelte's go-to framework. It adds key features like routing, layouts, and state management to Svelte.

Sveltekit allows you to make static sites, server-rendered sites, and even hybrid static-and-server-rendered web apps. It also has extremely fast hot reloading in development mode because of the way it bundles JavaScript.

## Getting started

*This tutorial draws heavily from the [SK Incognito docs](https://sk-incognito.vercel.app/learn/getting-started). Big thanks to SK Incognito for documenting Sveltekit in development.*

### Prerequisites

This tutorial assumes you're already familiar with the basics of HTML, CSS, and JavaScript.

You'll need Node Node 12 LTS or Node 14 LTS and npm installed. Here's how to install Node and npm on [Mac](https://wsvincent.com/install-node-js-npm-mac/) and [Windows](https://wsvincent.com/install-node-js-npm-windows/).

### Setup

In your terminal, run this command:

```bash
npm init svelte@next svelte-kit-app
# replace `svelte-kit-app` with whatever you like
```

You'll get a warning in the terminal that Sveltekit is in development. Take heed.

In the options, do not use TypeScript, Less, or SCSS. As Sveltekit is still in alpha, we'll stick with plain 'ol CSS and JavaScript (for now).

Once the setup is complete, cd into your new folder and install dependencies:

```bash
npm i
```

Then run the app in dev mode:

```bash
npm run dev
```

You can now view Svelte's classic counter app in your browser at [http://localhost:3000](http://localhost:3000/).

## Make a new homepage

In Sveltekit, every page is a Svelte component. Svelte components are identified by their .svelte extension. Pages are stored in `~/src/routes`, and every component in this directory is a page in your app (we'll discuss that more in our section on routing, below).

To get started, we'll overwrite the content of `~/src/routes/index.svelte` (the app's homepage).

Erase everything on that page, and replace it with a simple heading, like this:

```html
<main>
  <h1>Homepage</h1>
</main>
```

When you click save, your browser should update instantly to display the new homepage:

![Screenshot of a Sveltekit app with the heading ](https://images.prismic.io/wroom/35cb339c-0378-4d17-b90e-a4dcbffaa6ee_Screen+Shot+2021-03-22+at+2.49.01+PM.png?auto=compress,format)

For starters, we can edit this page as normal HTML. Try adding some content in-between the main tags to see what happens.

Then, try adding a style tag, with some CSS inside:

```html
<main>
  <div class="container">
    <h1>Homepage</h1>
    <hr>
    <p>This is my new Sveltekit app.</p>
  </div>
</main>

<style>
  main {
    font-family: sans-serif;
  }

  .container {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0px 20px;
  }

  .container > * {
    width: 100%;
    max-width: 700px;
  }
</style>
```

## Add interactivity

Svelte makes it easier to create interaction between JavaScript and HTML. Declare a variable in your JavaScript and then include it in your HTML in curly braces.

Try updating your homepage like this:

```html
<!-- ~/src/routes/index.svelte -->

<script>
  let number = 2;
</script>

<main>
  <div class="container">
    <h1>{number}</h1>
    <hr>
    <p>This is my new Sveltekit app.</p>
  </div>
</main>

<style>
  main {
    font-family: sans-serif;
  }

  .container {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0px 20px;
  }

  .container > * {
    width: 100%;
    max-width: 700px;
  }
</style>
```

You can make your app interactive by binding a variable to an input. Try replacing your `<script>` and `<main>` sections with this:

```html
<script>
  let text = 'Homepage';
</script>

<main>
  <div class="container">
    <h1>{text}</h1>
    <input bind:value={text}>
  </div>
</main>
```

This should create an input. When you change the value of the input, it should change the text in the heading.

In our app, we'll use JavaScript to fetch content from an API and display that content in HTML. In the next step, we'll set up a content API with Prismic.

## Set up Prismic

If you don't already have a Prismic account, [create one](https://prismic.io/dashboard/signup?redirectUri=/dashboard) (it's free). Then, visit [prismic.io/dashboard](http://prismic.io/dashboard), and create a new repo.

*Note: If you want to skip this step, you can use the Prismic repository that we're using in these examples. Just skip to the next section, "Fetch content in Svelte," and use this URL for your API endpoint: `https://svelte-tutorial.cdn.prismic.io/api/v2`*

In your new repo, create a repeatable Custom Type called "page".

Add:

-   a Key Text field called "title",
-   a UID field called "uid",
-   an Image field called "image",
-   and a Rich Text field called "content".

*✨ For a shortcut, you can copy-paste [this JSON](https://gist.github.com/samlfair/327314b90b7f220c17fb0cb9250fd935) into the JSON Editor in the Custom Type builder, and it will configure all of these fields for you.*

Now, go back to the main page of your repository and create your first document. Give it the title and UID "homepage". Then, add an image and some content, and click Save and Publish.

![Screenshot of the Prismic editor.](https://images.prismic.io/wroom/4ef2cd14-7d7a-410a-88ef-e2d036c0e326_Screen+Shot+2021-03-22+at+3.11.45+PM.png?auto=compress,format)

In a new tab, go to `<your-repo-name>.prismic.io/api/v2`. This will open your API browser, allowing you to view your content on the Prismic API. Click Search documents, and the document you just published should appear:

![Screenshot of the Prismic API browser.](https://images.prismic.io/wroom/210fbb26-fc96-45c2-8f6c-b6a628493b97_Screen+Shot+2021-03-22+at+3.17.22+PM.png?auto=compress,format)

Next, we're going to use Prismic's dev tools to fetch content and render it in Svelte.

## Fetch content in Svelte

We will use two packages to work with Prismic in Svelte. `@prismicio/client` is Prismic's basic JavaScript package for fetching content from the API, and prismic-dom is Prismic's basic package for rendering content in the DOM.

To install them, run:

```bash
npm i -D @prismicio/client prismic-dom
```

Next, create a directory at the root of your project, called `utils/`. Inside, create a file called `client.js`, and paste in this code:

```javascript
import Prismic from '@prismicio/client';
const apiEndpoint = 'https://your-repo-name.cdn.prismic.io/api/v2';
const Client = Prismic.client(apiEndpoint);

export default Client;
```

Make sure to update your repo name in the `apiEndpoint` variable.

Now, in our homepage component, `~/src/routes/index.svelte`, we'll add our new utilities. Update your component like this:

```html
<!-- ~/src/routes/index.svelte -->

<script context="module">
  import Client from './../../utils/client';
  import PrismicDom from 'prismic-dom';

  export async function load() {
    const document = await Client.getByUID('page','homepage');
    return {
      props: {
        document,
      }
    };
  }
</script>

<script>
  export let document;
</script>

<main>
  <div class="container">
    <h1>Homepage</h1>
    <pre>{JSON.stringify(document, null, 2)}</pre>
  </div>
</main>

<style>
  main {
    font-family: sans-serif;
  }

  .container {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0px 20px;
  }

  .container > * {
    width: 100%;
    max-width: 700px;
  }
</style>
```

What's happening here?

The most important thing is the `load()` function. This function accepts information about the current page as arguments and returns props that you can use in your app.

Inside that function, we're using the Prismic query helper `Client.getByUID()` to fetch your homepage document from the API and then returning that document as a prop.

Then, in a separate script tag, we initialize the response variable, which makes the variable accessible in our app.

If everything worked, you should now see your raw API response on the page.

Of course, we don't want to display JSON. Instead, let's display some human-readable content, by updating the `<main>` tag, like this:

```html
<main>
  <div class="container">
    <h1>{document.data.title}</h1>
  </div>
</main>
```

Now, the title of your document is coming directly from Prismic.

Prismic provides utilities for working with Rich Text. `prismic-dom` will convert your Rich Text field to HTML. Then, you can use Svelte's `{@html }` utility to render it.

Update your `<main>` tag like this:

```html
<main>
  <div class="container">
    <h1>{document.data.title}</h1>
  </div>
</main>
```

Now you should have some content on your page:

![](https://images.prismic.io/wroom/e3de784c-fccb-484e-9754-3fd752236c03_Screen+Shot+2021-03-22+at+3.47.14+PM.png?auto=compress,format)

## Style your content

Let's bind an inline style. We'll create a header section and use the image from Prismic as the background:

```html
<!-- ~/src/routes/index.svelte -->

<script context="module">
  import Client from './../../utils/client';
  import PrismicDom from 'prismic-dom';

  export async function load() {
    const document = await Client.getByUID('page','homepage')
    return {
      props: {
        document,
      }
    };
  }
</script>

<script>
  export let document;
</script>

<main>
  <div class="header container" style="background-image: url('{document.data.image.url}')">	 
    <h1>
      {document.data.title}
    </h1>
  </div>
  <div class="container">
    <div class="text">
      {@html PrismicDom.RichText.asHtml(document.data.content)}
    </div>
  </div>
</main>

<style>
  main {
    font-family: sans-serif;
  }

  .container {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0px 20px;
  }

  .container > * {
    width: 100%;
    max-width: 700px;
  }

  .header {
    color: white;
    background-size: cover;
    min-height: 25vw;
    padding-top: 2rem;
    justify-content: flex-end;
  }
</style>
```

This looks good, but we've still got some unnecessary padding around the whole page. We can eliminate that with a CSS reset. In Svelte, CSS is scoped to the component. That means that the CSS in the component only styles that component. To style the document's `<body>` tag (where the padding is), we need to import a style sheet.

For starters, create the folder `~/src/styles/`, and then add a file called `reset.css`. Inside that file, paste the following code:

```css
/* ~/static/styles/reset.css */

body {
  margin: 0;
  padding: 0;
}

* {
  box-sizing: border-box;
}
```

Now, we need to import those styles to the component. You can do this by importing a CSS file in the component's second `<script>` tag, like this:

```html
<script>
  import "./../styles/reset.css";
  export let document;
</script>
```

When the page reloads, the hero image should be full-width.

## Create a layout

However, we know that we want the CSS reset on every page of the website. We can abstract it out of our component by moving it to a layout.

If you have content that appears on every page --- like a header and footer --- you can move it into a layout file. To create a layout, create the file `~/src/routes/$layout.svelte`. In that file, paste this code:

```html
<!-- ~/src/routes/$layout.svelte -->

<script>
  import "./../styles/reset.css";
</script>

<div class="flex-layout">
  <slot></slot>
  <p class="footer">© Acme Corp, 1951</p>
</div>

<style>
  .footer {
    margin: 3rem 0;
    text-align: center;
  }

  .flex-layout {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }
</style>
```

The `<slot>` element is where your page component will be inserted.

You can now remove the CSS reset from `~/src/routes/index.svelte`.

We can abstract more styles to the layout by creating a file called globals.css in `~/src/styles/` and importing that CSS to your layout:

```html
<script>
  import "./../styles/reset.css";
  import "./../styles/globals.css";
</script>
```

For instance, you can delete the `main` and `.container` style rules from `index.svelte` and these rules to `~/src/styles/globals.css`:

```css
body {
  font-family: sans-serif;
}

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0px 20px;
}

.container > * {
  width: 100%;
  max-width: 700px;
}
```

You've now created a page of your website with JavaScript and CSS.

![A screenshot of a homepage with a wide banner image.](https://images.prismic.io/wroom/b5bd8220-f674-4f68-9645-117ec1dff7b3_Screen+Shot+2021-03-22+at+5.00.14+PM.png?auto=compress,format)

Next, we'll look at how to add more pages.

## Add multiple pages

In Svelte, every file in `~/src/routes/` represents a route in your app, so `~/src/routes/dogs/daschund.svelte` corresponds to `your-site.com/dogs/daschund`.

File and folder names in square brackets are dynamic routes. So, the file `~/src/routes/dogs/[breed].svelte` would generate the route `/dogs/*`. If you visit `/dogs/doberman`, you will get the `[breed].svelte` file.

Similarly, folder names can be dynamic. So, you could have `~/src/routes/[pet]/[breed].svelte`, which would be rendered for both `/fish/goldfish` and `/cat/siamese`.

To proceed, copy the contents of `~/src/routes/index.svelte` into a new file in the same folder called `[uid].svelte`.

In `[uid].svelte`, modify the `load()` function to destructure the page prop (the props include information about the current page) like this:

```javascript
export async function load({ page }) {
    const { uid } = page.params;
    const document = await Client.getByUID('page','homepage');
    return {
      props: {
        document,
        uid
      }
    };
  }
```

`page.params` is an object containing the variables from the URL path, which are specified in the file and folders with square brackets around their names. In the example above, `/fish/goldfish` would have a `page.params` object like this:

```javascript
{
  pet: "fish",
  breed: "goldfish"
}
```

So, in the `load()` function, we extract the `uid` parameter from the page route and add it to the `props` object, which the function returns.

Then, add `uid` to the export statement in the second `<script>` tag:

```html
<script>
  export let document, uid;
</script>
```

Finally, we can use the `uid` variable somewhere in the template:

```html
<main>
  <div class="header container" style="background-image: url('{document.data.image.url}')">	 
    <h1>
      {uid}
    </h1>
  </div>
  <div class="container">
    <div class="text">
      {@html PrismicDom.RichText.asHtml(document.data.content)}
    </div>
  </div>
</main>
```

Now, navigate to another page at the root-level of your website, like `/doberman` or `/tabby`. If everything is working, you should see the `uid` param in your page:

![A screenshot of a webpage with ](https://images.prismic.io/wroom/0471b55c-ca22-4e52-9a6b-3c4fa4097f8c_Screen+Shot+2021-03-22+at+5.01.59+PM.png?auto=compress,format)

However, it's not very useful to directly display the param on the page. Instead, we want to use it to dynamically display content.

In the Prismic query function, replace "homepage" with the `uid` variable. Now, Svelte will query content from Prismic depending on the page path:

```javascript
export async function load({ page }) {
  const { uid } = page.params;
  const document = await Client.getByUID('page',uid);
  return {
    props: {
      document,
      uid
    }
  };
}
```

Right now, there is only one document in Prismic. Go back to your Prismic repo, and create a page with the UID "about", and add some content to the page. Now, if you go to `/about`, you should see your new about page.

In your `<main>` tag, remove the `uid` variable and replace the document title:

```html
<main>
  <div class="header container" style="background-image: url('{document.data.image.url}')">	 
    <h1>
      {document.data.title}
    </h1>
  </div>
  <div class="container">
    <div class="text">
      {@html PrismicDom.RichText.asHtml(document.data.content)}
    </div>
  </div>
</main>
```

## Create a nav component

Finally, we can add some site navigation. Let's create a nav component in `~/src/lib/`, called `nav.svelte`. Paste in the following code:

```html
<!-- ~/src/lib/nav.svelte -->

<nav class="container">
  <div class="menu">
    <a href="/">Home</a>
    <a href="/about">About</a>
  </div>
</nav>

<style>  
  nav {
    width: 100%;
    padding-top: 25px;
    padding-bottom: 25px;
    text-shadow: 0px 1px 3px rgba(0,0,0,.8), 0px 0px 6px rgba(0,0,0,.8);
  }
  
  nav a {
    color: white;
    text-decoration: none;
    padding-right: 1rem;
  }
</style>
```

Then, in your layout file, import the `Nav` component and update your HTML and CSS like so:

```html
<!-- ~/src/routes/$layout.svelte -->

<script>
  import Nav from "./../lib/nav.svelte"
  import "./../styles/reset.css";
  import "./../styles/globals.css"
</script>

<div class="flex-layout">
  <header class="absolute">
    <Nav />
  </header>
  <slot></slot>
  <p class="footer">© Acme Corp, 1951</p>
</div>

<style>
  .absolute {
    position: absolute;
    width: 100%;
  }

  .footer {
    margin: 3rem 0;
    text-align: center;
  }

  .flex-layout {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
  }
</style>
```

Now, you should have two pages that you can click before and forth between:

![](https://images.prismic.io/wroom/3c4846e9-0384-423d-8ce9-b85d1a003178_Screen+Shot+2021-03-22+at+5.19.54+PM.png?auto=compress,format)

## Deploy your site

For now, deploying with Sveltekit is a little tricky, as it is still in development. Sveltekit has a handful of "adapters" to handle deployment in different environments. For now, we'll deploy statically on Netlify.

To install the necessary adapter, run the following command:

```bash
npm i -D @sveltejs/adapter-static
```

Then, in `~/svelte.config.cjs`, set the adapter:

```javascript
const static = require('@sveltejs/adapter-static');
const pkg = require('./package.json');

/** @type {import('@sveltejs/kit').Config} */
module.exports = {
  kit: {
    // By default, `npm run build` will create a standard Node app.
    // You can create optimized builds for different platforms by
    // specifying a different adapter
    adapter: static(),

    // hydrate the <div id="svelte"> element in src/app.html
    target: '#svelte',

    vite: {
      ssr: {
        noExternal: Object.keys(pkg.dependencies || {}),
      },
    },
  },
}
```

Next, make sure your project is pushed to Github. You'll need to initialize your project as a git repository:

```bash
git init
```

Add all of your files to staging:

```bash
git add .
```

And commit them:

```bash
git commit -m "Init"
```

In GitHub, create a new repository and follow the instructions to push your project to GitHub.

Visit Netlify and create an account or log in. Click New site from Git. Follow the instructions to deploy your new GitHub repo.

For the deploy settings, ensure the build command is npm run build, and the build directory is build.

Once you deploy your site, it should be live!

![](https://images.prismic.io/wroom/637de1a9-336a-4a80-bbac-4771f4cd82f9_Screen+Shot+2021-03-23+at+7.58.40+PM.png?auto=compress,format)

## See a bug?

If you notice a bug in this tutorial, please add a comment or get at me on Twitter: [@samlfair](https://twitter.com/samlfair)

Thanks for reading :)

## Resources

To keep learning, check out these resources:

-   [The code for this project on GitHub](https://github.com/samlfair/svelte-tutorial)
-   [Prismic's JavaScript documentation](https://prismic.io/docs/technologies/javascript)
-   [Official Svelte docs](https://svelte.dev/)
-   [Official Sveltekit docs](https://kit.svelte.dev/docs)
-   [Svelte Society Svelte Cheatsheet](https://sveltesociety.dev/cheatsheet/)
-   [SK Incognito Docs](https://sk-incognito.vercel.app/learn/getting-started)
-   [Sveltekit on GitHub](https://github.com/sveltejs/kit)

*This tutorial was originally published [on the Prismic blog](https://prismic.io/blog/svelte-sveltekit-tutorial).*