NEW

Prismic offers an ideal solution to feature your e-commerce products in your promotional landing pages or inspirational content. View more

How to Make a Website with SvelteKit

Written by Sam Littlefair in
concept
on March 22, 2021

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! SvelteKit 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, and the final project live on Netlify.

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 a little strange. Here's an example of a .svelte file:

<script>
  let name = 'world';
</script>

<h1>Hello {name}!</h1>

That will generate a component that looks like this:

Hello world svelte example in the browser

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?

SvelteKit is a back-end framework for Svelte. While Svelte handles code that runs in the browser — like interactivity and reactivity — SvelteKit gives you infrastructure for the server hosting your app. SvelteKit will provide routing, layouts, static-site generation, API endpoints, and other app features that can only run on a server.

SvelteKit is also great because it has extremely fast hot reloading in development mode. When you click save, changes can appear instantaneously.

Getting started

Prerequisites

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

You'll need Node and npm (or Yarn) installed. Here's how to install Node and npm on Mac and Windows.

Setup

In your terminal, run this command:

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.

The terminal will ask you "Which Svelte app template?" For this tutorial, select Skeleton project.

You'll be prompted with options to use TypeScript, ESLint, and Prettier. For this tutorial, we'll proceed without them. Answer no to all.

Once the setup is complete, open the folder in your code editor (I use VSCode).

Install dependencies:

npm install

Then run the app in dev mode:

npm run dev

You can now open a basic app in your browser at 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:

<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 "Homepage"

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:

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

<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:

<!-- ~/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:

<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 (it's free). Then, visit 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 repository for your API endpoint: "svelte-tutorial".

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 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.

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.

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 @prismicio/helpers is Prismic's basic package for rendering content in the DOM.

To install them, run:

npm install --save-dev @prismicio/client @prismicio/helpers

Next, create a new directory inside /src, called /lib. Inside, create a file called prismicio.js, and paste in this code:

import * as prismic from '@prismicio/client'

// Update your repository name here
const repositoryName = 'svelte-tutorial'

const createClient = (params) => {
  const client = prismic.createClient(repositoryName, params)

  return client
}

export default createClient

Make sure to update your repository name.

The prismicio.js file allows you to define settings for your Prismic API connection, to use globally in your app.

Now we'll create an endpoint to fetch content from your repo. Create the file ~/src/routes/index.js. Inside, paste in this code:

// ~/src/routes/index.js

import createClient from '$lib/prismicio'

export async function get({ fetch }) {
  const client = createClient({ fetch })
  const document = await client.getByUID('page', 'homepage')

  if (document)
    return {
      body: {
        document,
      },
    }

  return {
    status: 404,
  }
}

This endpoint is named index.js. It queries the API for a homepage component. If it finds a document, it returns it. Otherwise, it returns a status 404 (page not found). If it finds a document, it will provide the document to the page component named index.svelte — your homepage component.

What is the fetch parameter? SvelteKit provides a fetch method to endpoints, which you can use to perform queries. The Prismic client doesn't include a fetch method, so we pass SvelteKit's fetch method. That way, we can perform HTTP GET requests optimized for SvelteKit.

Update the homepage component like this:

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

<script>
  import * as prismicH from '@prismicio/helpers';
  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?

In the script tag, we're initializing the document variable, which will be updated with data from the index.js endpoint.

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:

<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. @prismicio/helpers will convert your Rich Text field to HTML. Then, you can use Svelte's {@html } utility to inject it into the page.

Update your <main> tag like this:

<main>
  <div class="container">
    <h1>{document.data.title}</h1>
    <div class="text">
      {@html prismicH.asHTML(document.data.content)}
    </div>
  </div>
</main>

Now you should have some content on your page:

Style your content

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

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

<script>
  import * as prismicH from '@prismicio/helpers';
  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 prismicH.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:

/* ~/src/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:

<script>
  import * as prismicH from '@prismicio/helpers';
  import "./../styles/reset.css";
  
  export let document;
</script>

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

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:

<!-- ~/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:

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

For instance, you can delete the main, .container, and .container > * style rules from index.svelte and these rules to ~/src/styles/globals.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.

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 yoursite.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 or /dogs/poodle, you will get the [breed].svelte file.

To proceed, copy the contents of ~/src/routes/index.js and ~/src/routes/index.svelte into new files in the same folder called [uid].js and [uid].svelte.

In [uid].js, modify the get() function to destructure the params (params is an object the page's dynamic path segments, including uid about the current page) like this:

// ~/src/routes/[uid].js

import createClient from '$lib/prismicio'

export async function get({ fetch, params }) {
  const client = createClient({ fetch })
  const { uid } = params
  const document = await client.getByUID('page', uid)

  if (document)
    return {
      body: {
        document,
      },
    }

  return {
    status: 404,
  }
}

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 params object like this:

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

So, in the get() function, we extract the uid parameter from the page route and use it to query a document from the API based on its UID. Now, Svelte will query content from Prismic depending on the page path:

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. (You don't need to do this is you're using the "svelte-tutorial" repository.) Now, if you go to /about, you should see your new about page.

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:

<!-- ~/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:

<!-- ~/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:

Deploy your site

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

git init

Add all of your files to staging:

git add .

And commit them:

git commit -m "Init"

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

Visit Netlify or Vercel and create an account or log in. Click New project or New site. Follow the instructions to deploy your new GitHub repo. You can leave the deploy settings as is.

Once you deploy your site, it should be live!

☝️ If you thought this was fun, you might also like 👉 Svelte vs. React in 2022: How to Choose the Best Match for You

Resources

To keep learning, check out our official documentation for building a website with Prismic and Svelte and these handy resources:

Sam Littlefair

Sam Littlefair

Sam is a Canadian in France preoccupied with journalism, web publishing, JavaScript, meditation, and travel. He works on documentation at Prismic.

More posts