SvelteKit forms and endpoints work a little differently to other frameworks, like Astro or Remix, so it is worth taking a detailed look at how to pass form data from the client browser to your app’s running server instance. To help here, we look at code for a spelling, punctuation and grammar checking tool using LanguageTool. LanguageTool is a service with similar features to Grammarly and offers a free, but limited, API to check text snippets.
Initially, we will look at how you can get data from your server to the client with SvelteKit. Then, we look at how to run an initial grammar check from the client (before the page renders). LanguageTool’s free API limits requests on an IP address basis. This makes running checks from the browser, rather than the server, a good idea. That is because, even with a few users running checks simultaneously, limits could quickly be breached, letting no users complete checks. Running checks from the browser, allows each user’s check requests to be sent from that user’s own IP address and not the server’s.
To end, we will look at client-side and server forms. We use a client-side form to re-run the grammar check on updated (corrected) text. Then, the server-side form is used to submit the final version back to the server for saving to a database.
We will focus on the SvelteKit site, without drilling down into how the LanguageTool API interactions work. The full code is in the Rodney Lab GitHub repo, though, and you will find a link for this further down. For now, let’s get started on the app!
As you start out in SvelteKit, you might be a little overwhelmed by the different files that you can use for each route. SvelteKit works a little differently to Astro, Deno Fresh or Remix where typically, you can combine the server and client code into a single file. So let’s start by seeing what each file does and when you might need them.
Each SvelteKit route can have three files:
+page.svelte is home to the markup we want rendered for the page.
usually wrapped in a
<script> tag at the top of the
file. You will omit this file for a resource (or non-HTML route), like an XML sitemap, for
+page.ts is also code which runs on the client. There is a
difference between code here and code in the
tag in the
.svelte counterpart. We use this file to prepare any
data we want available before the initial render. In our grammar check example,
we query the LanguageTool API before rendering, and that initial API request is run in the
be used for API fetch requests. You might use this file instead of
+page.ts when you do not want to expose a secret API key to the client. Other use cases
are to pull data from a database running on the server or to handle user sessions for logged-in users. The final common use case is listening for form submissions. We use that here.
No. You will not always need all three, though in our example we see the three working in tandem
on the same route. We mentioned the
+page.svelte file would be
omitted for a non-HTML route. Similarly, if you have neither server forms nor secret API keys, you
might decide to omit the
+page.server.ts file. That pattern can
work well for a static site, or even, prerendered (static) pages on an otherwise, SSR
With the basics covered, let’s start looking at the app itself in more detail.
Our app only has a single route, and all three files sit right under the
routes folder. For a more realistic app, you would create a folder for each route (e.g.
src/routes/about/ and so on and so forth). In each folder, you
then place any of the three files needed for that route.
We keep things simple, and instead of pulling the text to check from a database (like we probably would in a real-world app), we have a static text string defined in the server route.
Avoid over-fetching here and only return data which the client requires. In the
real-world, you might check for a logged-in user then return text relevant to them (a draft post,
for example). Here, we return an object with a single text field. Next, we see this is available
+page.ts file, though if we omitted that file, we could
text from our
Remember, the code in
+page.ts loads before the page first
renders. We can run an initial grammar check here on the data which (would have been stored in the
app database). Here is the code for the page load file (
data argument of the
load function in line
4. This gives us access to the
text field returned from the server. Those results will be ready, ahead of the initial page render.
22, you can see we return the grammar check results, as well as the original text. These will now
be available in the
The final step is the Svelte markup (
Note, again we are pulling in data from the last step, (line
We can destructure the data into the parts we need (lines
12). This app only has a single page.
However, there is one peculiarity of SvelteKit you need to bear in mind. If you are working on a
+page.svelte file, for blog posts, as an example. The
first page visited will work fine and as expected. If the visitor then navigates to another page
(which uses the same template), the destructuring would not work. SvelteKit would not realize the
underlying data change with the page; it would present data for the first page. There is a
The first part is as before, and runs when the first page loads. The second (added block) takes
care of subsequent page loads. Both blocks are required here. As an alternative, you can avoid
data.text, for example, instead).
All is well and good so far! You might have noticed the text included one or two mistakes. We display the original text in a textarea, this is so the user can take on board the LanguageTool suggestions and update it. Once updated, they will probably want to recheck the text. If you’re thinking let’s submit the updated text to the server as a form, hold fire, we will see how to do that later. For now, we will run the check client side. This saves us a server trip.
Because we want to run a fetch request from the client, but after initial page load, we call the
LanguageTool API from the
<script> block in
Note a few features here:
We call our
handleRecheckfunction from the form
We bind the form textarea value to the
textvariable in line
33letting us access the updated text in the API call.
- Our server is not involved anywhere here, we are not using the platform, but this makes sense here.
Once the user is happy with the text, for sure they will want to save it. This is where we can use the platform! Here is the same
src/routes/+page.svelte file updated:
Notice the differences in the form element:
method="post"attributes on the form element
textinput to pass the updated text value back to the server (alternative is to include text as a query parameter in the
actionvalue on the form element)
10, we pull in the data expected in response to the form submission, and use it to provide user feedback
Of course, this will not work in isolation. Let’s jump back to where we started; the server
code for the route. There we add the form action code, returning the
form data we see above.
save name for our handler (line
5) matches the name we used for it in the form DOM element. This mechanism lets us add several
form handlers to the same route. The
fail function lets us provide
feedback when something is wrong with inputs. Finally, returning the custom
saved field in the response object is another way to provide user feedback, this time letting them
know it all went well.
We have run through the main constituent parts for passing data between server and client in SvelteKit. If you are new to this, you will probably want to clone the full code repo and play around with it. You do not need an API key to run the LanguageTool checks. Consider adding progressive enhancements to the server-side form.
In this post, we saw SvelteKit form handling and passing data between server and client. In particular, we saw:
why you might use a
how you can combine
+page.server.tspage server load with
- how to handle forms in SvelteKit both server-side and in the client browser
Please see the full repo code on the Rodney Lab GitHub repo . I do hope you have found this post useful and can use the code as a starting point for a tool to help cement your understanding. I also hope there is something you can take to an app you are currently working on, or one you plan to build in a future project. Let me know what you decide on! Also, please let me know about any possible improvements to the content above.
- SvelteKit uses up to three files for each route, and you can combine all three! +page.svelte is home to client markup. However, there might be some code you want to run on the client, but before the page first renders. Fetching data from an external source is a classic example here. Add that code to `+page.ts`. You might also have code you only want to run on the server, for example to protect an API key or even to listen for form submissions. Place that code in the route’s +page.server.ts file.
- Yes. If you have both `+page.server.ts` and `+page.ts`, you might need to access server returned data in `+page.ts`. This is also possible: destructure the `data` prop from the `+page.ts` load function parameter. Your server returned data will be available there.
- Here, a SvelteKit Action is your friend. In the route `+page.server.ts` file, export an action object. This will have a member for each form you want to listen for, on the route. As an example, if you include an `action="?/save"` attribute on the form element, then the handler key (in actions) will be `save`. The action value will be a handler function which can return values for client feedback. You can access those returned values from the `form` prop in the route’s `+page.svelte` file.
If you have found this post useful, see links below for further related content on this site. I do hope you learned one new thing from the video. Let me know if there are any ways I can improve on it. I hope you will use the code or starter in your own projects. Be sure to share your work on Twitter, giving me a mention, so I can see what you did. Finally, be sure to let me know ideas for other short videos you would like to see. Read on to find ways to get in touch, further below. If you have found this post useful, even though you can only afford even a tiny contribution, please consider supporting me through Buy me a Coffee.
Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter and also askRodney on Telegram . Also, see further ways to get in touch with Rodney Lab. I post regularly on SvelteKit as well as Search Engine Optimization among other topics. Also, subscribe to the newsletter to keep up-to-date with our latest projects.