SvelteKit Remote Functions Tips and Techniques

November 10, 2025 Updated: November 12, 2025

SvelteKit Remote Functions Tips and Techniques

Table of Contents

Introduction

This post covers practical tips for using remote queries, commands, and forms in SvelteKit.

Forms

Remote Forms vs Superforms

Superforms is a great library for handling forms in SvelteKit. It can do things that remote forms can’t. When using Superforms’s json mode, it converts the form data into a JSON object that is serialized with the devalue library and sent to the server. Unlike FormData, which only supports string | Blob values, the JSON object can contain any type of value supported by transport hooks.

Superforms also uses JSONSchema adapters to obtain runtime information about your schema. This allows it to provide validation attributes like required, min, max, minlength, maxlength, and pattern. It also allows it to know when a field should be coerced to other types like null, boolean, number, Date, etc.

Remote forms, on the other hand, are a bit more limited. They only send FormData to the server. Aside from number and boolean checkbox inputs, remote forms does not handle any coercion of values. That is why your schema must only include the following input types: string, string[], number, boolean, File, and File[]. Coercion to other output types must be handled by your schema.

undefined is also supported as an input type, because some fields may be excluded from the FormData such as boolean checkbox or fields that are conditionally rendered in the UI. However, adding .optional() or .undefined() to a schema for a field that is always present in the FormData doesn’t actually do anything. It will still be included in the FormData as an empty string. All it does is incorrectly misrepresent the input types.

Remote forms also use StandardSchemaV1, which is just types. It does not provide any runtime information about your schema. So it cannot automatically provide validation attributes. If you have a field that is nullable, remote forms won’t know that until the validation actually runs. The value that the schema receives will always be one of the types mentioned in the previous two paragraphs.

Dates

Dates are a bit tricky to handle. The reason SvelteKit doesn’t automatically coerce date inputs to Date objects, is because the coercion is handled on the server, which could be in a different timezone than the client. This could cause issues with date parsing and validation.

A date input uses the format YYYY-MM-DD and the datetime-local input uses the format YYYY-MM-DDTHH:MM. Since this does not include the client’s timezone information, the date should be converted to a UNIX timestamp in the client instead or the form should include the timezone as a hidden input. Otherwise, the conversion to Date on the server could be incorrect.

For that, this function could be useful:

Discriminated Unions

Because the fields property of a form instance is a proxy object (with values returned as methods), you can’t use discriminated unions in the usual way. You’ll need to use type assertions to make them work properly.

Imperative Validation

Sometimes you need to validate a form schema that requires additional data. One way to do this, would be to use an async refinement (zod) or check (valibot) in your schema that fetches the data when the schema is validated.

Alternatively, you can use the invalid method provided by the remote form to perform additional validation and report the issues to the client.

If you want to wait to validate the entire schema until inside the remote function, because you want to fetch data before the schema is validated, then you can use the standard schema validator and pass the entire issues array to the invalid method.

Utilities

Guarded Remote Functions

Guarded remote functions ensure that your remote functions are only called by authenticated users. Copy the following code into a new file called $lib/server/remote.ts and import the guardedQuery(), guardedCommand(), and guardedForm() functions into your remote functions code.

Lint Rule to Enforce Guarded Remote Functions

The lint rule to enforce guarded remote functions is a simple ESLint rule that checks that all exports in .remote.ts files use guardedQuery(), guardedCommand(), or guardedForm(). Direct exports of query(), command(), or form() are not allowed unless you explicitly opt out for a given remote function using eslint-disable-next-line custom/enforce-guarded-functions.