Learn how to build modern web applications with SvelteKit, a quickly growing framework for generating static web pages (SSG) as well as Server Side Rendered content (SSR). In this crash course, you’ll learn all the basics including file-based routing, API routes, dynamic routes, global and scoped CSS, and much more!
Source Code
https://github.com/brittneypostma/sveltekit-starter
What is Svelte?
- Svelte is a component framework that allows you to break up your application into reusable chunks.
- Svelte is similar to React & Vue, with one key difference, no virtual DOM. Instead of letting the browser do the work, Svelte compiles itself away during the build process into vanilla html, css, and javascript.
- Easy global state management with Svelte stores.
Why SvelteKit?
- The goal with SvelteKit is to have a single recommended path for building everything from high performing web apps to static blog pages.
- With SvelteKit we get file-system based routing, optional rendering (SSR & SSG), code-splitting, CSS scoping by default, the ability to completely disable JavaScript and more.
Getting Started with a new SvelteKit app
npm create svelte
At the time of recording, SvelteKit is in a release candidate phase, which means there are no more planned breaking changes.
Here we create a skeleton project without the TypeScript, ESLint, or Prettier configurations.
cd my-app
npm install
npm run dev -- --open
Aliases
SvelteKit with Vite provides the ability to setup aliases to folders within your app to make it easier to path to different areas. I have an article on how to set those up if you want to learn more.
Create the lib
folder within the src
folder and add a new file, header.svelte
to it. Inside header.svelte
, we are going to add a navigation bar for the site.
<header>
<nav>
<ul>
<li>
<a href="/">Home</a>
</li>
<li}>
<a href="/about">About</a>
</li>
<li>
<a href="/blog">Blog</a>
</li>
</ul>
</nav>
</header>
Now we need to create the routes that go to these respective links.
Routes
Inside the routes folder, we already have an +page.svelte
file that matches to the home or /
route. We need to add the /about
route by creating a new folder named about
and a new file inside +page.svelte
. We will add the /blog
route later since it is a dynamic route.
Layout
We don’t want to have to copy and paste our header onto each page. SvelteKit provides a wrapper file named +layout.svelte
that will wrap the entire application in whatever is in that file. Inside of the routes folder, create a new file named +layout.svelte
and your app will disappear off your screen if you are running the dev server. This is because Svelte needs a <slot/>
component to know where to insert the child content to. Add this code to your +layout.svelte
file to see the header and content on the page.
<script>
import Header from '$lib/header.svelte';
</script>
<header />
<main>
<slot />
</main>
Styling in Svelte
Styling in Svelte is scoped by default to each of the components. If you use an element selector like p
inside a component, then it will only effect the p
tags within that component. You can also use global styling by creating a global css file and importing it into the +layout.svelte
file. Grab the global css I used https://github.com/brittneypostma/sveltekit-starter/blob/main/src/global.css and place it into the src
folder. Then, import the file into +layout.svelte
like this.
<script>
import '../global.css';
import Header from '$lib/header.svelte';
</script>
Now the app should start looking a little better.
SvelteKit Context Module
https://kit.svelte.dev/docs#ssr-and-javascript
SvelteKit provides us with different ways to render our application on a page by page basis, by default, it will be rendered on the server and sent to the client as HTML. For the home route, +page.svelte
, we only need the content to be statically rendered. There will be no JavaScript added to the page at all. We can add this block to the top of the +page.js
route to change it.
export const prerender = true;
Counter component
Grab the counter component - https://github.com/brittneypostma/sveltekit-starter/blob/main/src/lib/counter.svelte and drop it into the lib
folder. Inside the counter.svelte
file, we can see one of the default animations that Svelte provides out of the box along with some of the sugar syntax Svelte has to handle JavaScript inside of your markup. Now, let’s add it to the +page.svelte
route to see it on the page.
<script>
import Counter from '$lib/counter.svelte';
</script>
<section>
<h1>Welcome!</h1>
<h2>Let's learn Svelte</h2>
<Counter />
</section>
<style>
section {
min-height: calc(100vh - var(--header-height));
display: grid;
place-items: center;
place-content: center;
}
</style>
Static assets
Add the static folder to the root of your application. By default, Svelte will path to that static folder, so you don’t have to do relative pathing for assets in your app.
Class Directives and Page Store
Svelte will purge any CSS classes that are unused in your components and provides a $app/page
store that allows you to tell which route you are on currently. Using this, let’s finish out the header.svelte
file.
<script>
import { page } from '$app/stores'
</script>
<header>
<div class="left">
<img src="svelte-logo.svg" alt="svelte logo" />
</div>
<nav>
<ul>
<li class:active={$page.path === '/'}>
<a href="/">Home</a>
</li>
<li class:active={$page.path === '/about'}>
<a href="/about">About</a>
</li>
<li class:active={$page.path === '/blog'}>
<a href="/blog">Blog</a>
</li>
</ul>
</nav>
<div class="right" />
</header>
<style>
.left img {
margin-left: 1rem;
width: 2rem;
height: 2rem;
object-fit: contain;
}
header {
height: var(--header-height);
display: flex;
justify-content: space-between;
align-items: center;
}
nav {
background: var(--color-primary);
box-shadow: var(--shadow-lg);
padding: var(--md) var(--xl);
border-radius: 0 0 var(--xl) var(--xl);
}
ul {
display: flex;
justify-content: space-between;
gap: var(--base);
}
a {
text-shadow: var(--shadow-text);
}
.active {
border-bottom: 2px solid var(--color-accent);
}
</style>
Hydration and Routing
The browser
store in the video is no longer correct. You can set router
to false
to enable this feature now. Also, hydrate
has changed to a boolean instead of the environment.
SvelteKit includes a client-side router that will take control of user interactions like links and the browser back and forward buttons instead of allowing the browser to handle the navigation by reloading. This can be turned off to allow the browser to take control by setting export const router = false
in the endpoint.
Hydration in SvelteKit typically hands off the server-rendered HTML to the client to implement any JavaScript needed on the page. If you don’t need JavaScript for the page, you can turn off hydration by setting it to false
If both hydrate
and router
are false
, there will not be any JavaScript added to your page at all.
Svelte provides a <svelte:head>
component to include things like the title and social images in the head
section of the html on the page level. These get injected at compile time. Add a new +page.js
file inside of the about
directory.
import { browser, dev } from '$app/env';
export const hydrate = dev;
export const router = browser;
export const prerender = true;
Dynamic Routes
Sometimes, we need a route that will catch more than one url. SvelteKit can do this by using square brackets around the parameter you want to catch, [params]
. For our blog route, we can create a folder blog
inside of routes
. In the blog
folder, we need an +page.svelte
file that will match the /blog
route. Then we need a folder inside blog
called [slug]
with square brackets around it, this means it is a dynamic route. Then inside create another +page.svelte
file.
Load function
If a route needs to fetch data to render for the page or perform any logic around routing, you will need to use the load
function inside of the +page.js
file. The load
function runs before a component is created. It runs during the server-side rendering and again in the client allowing you to get data for a page without showing a loading spinner or needing the onMount
lifecycle function. We are going to fetch the data from the dev.to api to grab my posts available.
export async function load({ fetch }) {
const response = await fetch(`https://dev.to/api/articles?username=bdesigned`);
const posts = await response.json();
return {
posts
};
}
Props
We return the posts as props and then need to accept the props into the component in a normal script tag inside +page.svelte
like this.
<script>
export let data;
const posts = data.posts;
</script>
We can then finish off the markup.
<svelte:head>
<title>Blog</title>
</svelte:head>
<section>
<h1>Blog</h1>
<ul>
{#each posts as post}
<li>
<a href={`/blog/${post.id}`}> {post.title}</a>
</li>
{/each}
</ul>
</section>
Slug Route
In the dynamic route we created we need to do a similar load function, but also grab the params from the slug to create the correct route. Create a +page.js
file inside the [slug]
route. Then, we can use the page
store provided in the load function to grab the params.
export async function load({ page, fetch }) {
const response = await fetch(`https://dev.to/api/articles/${page.params.slug}`);
const post = await response.json();
return {
post
};
}
Grab our post prop.
<script>
export let data;
const post = data.post;
</script>
And finally use the Svelte template syntax {@html}
to parse the html onto the page.
<svelte:head>
<title>{post.title}</title>
</svelte:head>
<article>
<h1>{post.title}</h1>
{@html post.body_html}
</article>
That is it! We now have a working svelte blog that pulls in data from the dev.to api. Check out the docs on adapters to find out more on building and hosting your site.