Skip to main content

Hydratable data

In Svelte, when you want to render asynchonous content data on the server, you can simply await it. This is great! However, it comes with a major pitall: when hydrating that content on the client, Svelte has to redo the asynchronous work, which blocks hydration for however long it takes:

<script>
  import { getUser } from 'my-database-library';

  // This will get the user on the server, render the user's name into the h1,
  // and then, during hydration on the client, it will get the user _again_,
  // blocking hydration until it's done.
  const user = await getUser();
</script>

<h1>{user.name}</h1>

That’s silly, though. If we’ve already done the hard work of getting the data on the server, we don’t want to get it again during hydration on the client. hydratable is a low-level API build to solve this problem. You probably won’t need this very often -- it will probably be used behind the scenes by whatever datafetching library you use. For example, it powers remote functions in SvelteKit.

To fix the example above:

<script>
  import { hydratable } from 'svelte';
  import { getUser } from 'my-database-library';

  // During server rendering, this will serialize and stash the result of `getUser`, associating
  // it with the provided key and baking it into the `head` content. During hydration, it will
  // look for the serialized version, returning it instead of running `getUser`. After hydration
  // is done, if it's called again, it'll simply invoke `getUser`.
  const user = await hydratable('user', getUser());
</script>

<h1>{user.name}</h1>

This API can also be used to provide access to random or time-based values that are stable between server rendering and hydration. For example, to get a random number that doesn’t update on hydration:

import { import hydratablehydratable } from 'svelte';
const const rand: anyrand = import hydratablehydratable('random', () => var Math: Math

An intrinsic object that provides basic mathematics functionality and constants.

Math
.Math.random(): number

Returns a pseudorandom number between 0 and 1.

random
());

If you’re a library author, be sure to prefix the keys of your hydratable values with the name of your library so that your keys don’t conflict with other libraries.

Imperative API

If you’re writing a library with separate server and client exports, it may be more convenient to use the imperative API:

import { import hydratablehydratable } from 'svelte';

const const value: anyvalue = import hydratablehydratable.get('foo'); // only works on the client
const const hasValue: anyhasValue = import hydratablehydratable.has('foo');
import hydratablehydratable.set('foo', 'whatever value you want'); // only works on the server

Custom serialization

By default, Svelte uses devalue to serialize your data on the server so that decoding it on the client requires no dependencies. If you need to serialize additional things not covered by devalue, you can provide your own transport mechanisms by writing custom encode and decode methods.

encode

Encode receives a value and outputs the JavaScript code necessary to create that value on the client. For example, Svelte’s built-in encoder looks like this:

import * as module "devalue"devalue from 'devalue';

/**
 * @param {any} value
 */
function function encode(value: any): string
@paramvalue
encode
(value: any
@paramvalue
value
) {
return module "devalue"devalue.function uneval(value: any, replacer?: (value: any, uneval: (value: any) => string) => string | void): string

Turn a value into the JavaScript that creates an equivalent value

uneval
(value: any
@paramvalue
value
);
} function encode(value: any): string
@paramvalue
encode
(['hello', 'world']); // outputs `['hello', 'world']`

decode

decode accepts whatever the JavaScript that encode outputs resolves to, and returns whatever the final value from hydratable should be.

Usage

When using the isomorphic API, you must provide either encode or decode, depending on the environment. This enables your bundler to treeshake the unneeded code during your build:

<script>
  import { hydratable } from 'svelte';
  import { BROWSER } from 'esm-env';
  import { encode, decode } from '$lib/encoders';

  const random = hydratable('random', () => Math.random(), { transport: BROWSER ? { decode } : { encode }});
</script>

For the imperative API, you just provide encode or decode depending on which method you’re using:

import { import hydratablehydratable } from 'svelte';
import { import encodeencode, import decodedecode } from '$lib/encoders';

const const random: anyrandom = import hydratablehydratable.get('random', { decode: anydecode });
import hydratablehydratable.set('random', var Math: Math

An intrinsic object that provides basic mathematics functionality and constants.

Math
.Math.random(): number

Returns a pseudorandom number between 0 and 1.

random
(), { encode: anyencode });

Edit this page on GitHub llms.txt