How to implement file upload with SvelteKit
Resources
SvelteKit version: 1.5.0
Svelte version: 3.54.0
GitHub repository: https://github.com/JustinyAhin/okupter-repos/tree/main/apps/sveltekit-file-upload
File upload is everywhere. And you've probably already encountered it during your web journey, whether as a simple user or a developer. In this article, we are going to learn how to implement a file-uploading feature in a Svelte application.
Overview of a file upload feature in JavaScript
Let's say we are building an application where users can upload their profile pictures. Once the picture is successfully uploaded, it should be added to a folder on the server. We can then access the picture on the server using its URL.
The process for this will be:
The user selects a picture from their computer through a file input in a form
The picture file is read, and its content is sent to the server
The server saves the picture in a folder
We can now access the picture using its URL
This is a very rough idea of how the feature works. They are many other steps like validation, error handling, displaying the upload progress, etc. that are necessary to make the feature robust. We will go into more detail about these steps later.

Our setup
We will use SvelteKit to create our Svelte application for this post. SvelteKit is a meta framework on top of Svelte that offers features like file-based routing, server, and API routes, etc.
To create a SvelteKit application, you can run the following command in your terminal:
pnpm create svelte@latest sveltekit-file-uploadYou can also read the official documentation here for more information.
Creating the file input
The first step for our feature is to create a form with a file input. A form in Svelte is pretty much similar to an HTML form. In the src/routes/+page.svelte of our new SvelteKit project, we can add the following code:
<form>
<div class="group">
<label for="file">Upload your file</label>
<input
type="file"
id="file"
name="fileToUpload"
required
/>
</div>
<button type="submit">Submit</button>
</form>This is just a simple file input with a label and a submit button. There are, however, a few things needed to make it work properly:
Validation for the allowed extensions for the file. Since we are uploading images, we will only allow
jpg,jpeg, andpng,webpfiles.A server action that runs when the form is submitted. This action will be responsible for reading and sending the file to the server.
The HTML file input has a property called accept, allowing us to specify the extensions. We can add it to our input like this:
<input type="file" id="file" name="fileToUpload" accept=".jpg, .jpeg, .png, .webp" required />We can also declare an array with these extensions and use it in the accept property:
<!-- src/routes/+page.svelte -->
<script lang="ts">
const authorizedExtensions = ['.jpg', '.jpeg', '.png', '.webp'];
</script>
<form>
<div class="group">
<label for="file">Upload yout profile picture</label>
<input type="file" id="file" name="fileToUpload" accept={authorizedExtensions.join(',')} required />
</div>
<button type="submit">Submit</button>
</form>It's time to create the server action to handle the file uploading.
Since we are uploading files to the server, we will use the multipart/form-data enctype for the form.
This attribute can now be added to the form element alongside with SvelteKit use:enhance to enable progressive enhancement and the POST method.
<form method="post" use:enhance enctype="multipart/form-data">
<div class="group">
<label for="file">Upload your file</label>
<input
type="file"
id="file"
name="fileToUpload"
accept={authorizedExtensions.join(',')}
required
/>
</div>
<button type="submit">Submit</button>
</form>Getting and uploading the file to the server
Now that the frontend part is done let's move to the backend part. SvelteKit form actions allow executing some logic once a form is submitted.
Actions are added in the +page.server.ts file for the route where they should be called.
A basic form action will look like this:
export const actions = {
default: async () => {
// Some logic
}
};In our case, we want to get the uploaded file, do some basic validation, and then upload the file to the desired location (a folder on the server, a remote location like S3 or Cloudflare R2, etc.).
Let's see what that looks like in practice:
import { fail } from '@sveltejs/kit';
export const actions = {
default: async ({ request }) => {
const formData = Object.fromEntries(await request.formData());
if (
!(formData.fileToUpload as File).name ||
(formData.fileToUpload as File).name === 'undefined'
) {
return fail(400, {
error: true,
message: 'You must provide a file to upload'
});
}
const { fileToUpload } = formData as { fileToUpload: File };
}
};Here, we are using native HTML FormData to get the data submitted from the front end. We are also returning a custom error in case there is no file uploaded.
For this example, we are just saving the file in the static folder of our SvelteKit app. For that, we will use the Node.js writeFileSync function.
import { writeFileSync } from 'fs';
export const actions = {
default: async ({ request }) => {
// ..........
// ..........
const { fileToUpload } = formData as { fileToUpload: File };
// Write the file to the static folder
writeFileSync(`static/${fileToUpload.name}`, Buffer.from(await fileToUpload.arrayBuffer()));
return {
success: true
};
}
}Wrapping up
If you have been following my blog, you should know that I like Svelte (and SvelteKit) because both try to stick as much as possible to web standards. And this example for file upload is a good example of that.
If you have some additional about this example, or you want me to write about another topic, feel free to reach out on justin@okupter.com or on Twitter.