Data & state
Data & remote functions
All six of Kit's remote function primitives work inside islands, including query, command and form.
On this page
An island on its own cannot reach the server, yet most interactive parts of a page need to read data, change it or receive a stream of updates. Because islands are ordinary Svelte components (see Regions), Kit’s remote functions work inside them without changes, and all six primitives (query, query.batch, query.live, command, form and prerender) run in an island exactly as they do in a Kit page. They take the place of the fetch calls and endpoints you would otherwise write by hand, and the server validates every call.
<script>
import { greeting } from './hello.remote';
const who = await greeting();
</script>
<p>{who}</p>In production, query results from the server render are seeded into the client cache, so an island that reads a query during SSR does not fetch it again when it hydrates. In dev this degrades to a refetch, as the note further down explains.
The seed is sized to its readers. A query result is seeded only when some island on the page can call that remote, which means its built chunks import the module. A lake, a server island or the page’s own script that awaits a query only for the server render ships HTML and no seed, so a whole CMS entry rendered through a lake doesn’t cost the page twice.
Try it
This counter is a client island that reads a query and changes the value through two commands. The value lives on the server, so each button press is a real round trip, after which a .refresh() reads the value again. Because the state is shared, if you open this page in a second tab, both tabs move.
let count = 3;
export const getCount = query(async () => count);
export const bump = command(async () => (count += 1));Forms and commands
form() and command() POST to their endpoints and work from inside an island. On success, form() runs Kit’s invalidateAll, which ogygia treats as a soft seed refresh: it fetches the current page again, merges <head> and refreshes the data seeds in place, without a view transition, a body swap, an island remount or clearing live queries.
<script>
import { addEntry } from './guestbook.remote';
</script>
<form {...addEntry}>
<input name="msg" />
<button>Sign</button>
</form>If an island needs a query to update after a mutation, call .refresh() on it, or pair submit().updates(query) with requested(query).refreshAll() on the server. updates on its own does not fill the response.
Mutation guards
Writing to a captured host variable from an island’s markup is a mistake, because the value is a serialized snapshot rather than live state. In dev, ogygia wraps each snapshot in a deep proxy that logs a console.warn with the prop’s path whenever you mutate it. The write still applies, since this is only a warning, but treat it as the signal to move that state into the island.
Dev is not prod
In dev, SSR query seeding degrades to a refetch, and the mutation proxy only runs in dev. Check data flows against a production build.
Do / don’t
- Do call
.refresh()(orsubmit().updates(q)plusrequested(q).refreshAll()on the server) when a mutation has to update a query.updateson its own does not fill the response. - Do test data flows against a production build, because seeding and the mutation guard behave differently in dev.
- Don’t mutate a captured host prop from an island’s markup. It is a serialized snapshot, so move mutable state inside the island.
Next chapter: collections turn your content into typed entries.