Skip to content

Commit 78011ba

Browse files
authored
feat(svelte): add support for async rendering (#14430)
* feat: add support for async rendering * Tidy up fixture * Format and dedupe
1 parent ef7fdb7 commit 78011ba

13 files changed

Lines changed: 257 additions & 58 deletions

File tree

.changeset/spicy-foxes-end.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
'@astrojs/svelte': minor
3+
---
4+
5+
Adds support for async server rendering
6+
7+
Svelte 5.36 added experimental support for async rendering. This allows you to use `await` in your components in several new places. This worked out of the box with client-rendered components, but server-rendered components needed some extra help. This update adds support for async server rendering in Svelte components used in Astro.
8+
9+
To use async rendering, you must enable it in your Svelte config:
10+
11+
```js
12+
// svelte.config.js
13+
export default {
14+
compilerOptions: {
15+
experimental: {
16+
async: true
17+
}
18+
}
19+
};
20+
```
21+
22+
Then you can use `await` in your components:
23+
24+
```svelte
25+
<script>
26+
let data = await fetch('/api/data').then(res => res.json());
27+
</script>
28+
<h1>{data.title}</h1>
29+
```
30+
31+
See [the Svelte docs](https://svelte.dev/docs/svelte/await-expressions) for more information on using `await` in Svelte components, including inside `$derived` blocks and directly in markup.

examples/framework-multiple/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"react": "^18.3.1",
2323
"react-dom": "^18.3.1",
2424
"solid-js": "^1.9.9",
25-
"svelte": "^5.38.10",
25+
"svelte": "^5.39.3",
2626
"vue": "^3.5.21"
2727
}
2828
}

examples/framework-svelte/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
"dependencies": {
1313
"@astrojs/svelte": "^7.1.1",
1414
"astro": "^5.13.10",
15-
"svelte": "^5.38.10"
15+
"svelte": "^5.39.3"
1616
}
1717
}

packages/integrations/svelte/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"scripts": {
3333
"build": "astro-scripts build \"src/**/*.ts\" && astro-scripts build \"src/editor.cts\" --force-cjs --no-clean-dist && tsc",
3434
"build:ci": "astro-scripts build \"src/**/*.ts\" && astro-scripts build \"src/editor.cts\" --force-cjs --no-clean-dist",
35-
"dev": "astro-scripts dev \"src/**/*.ts\""
35+
"dev": "astro-scripts dev \"src/**/*.ts\"",
36+
"test": "astro-scripts test \"test/**/*.test.js\""
3637
},
3738
"dependencies": {
3839
"@sveltejs/vite-plugin-svelte": "^5.1.1",
@@ -42,7 +43,8 @@
4243
"devDependencies": {
4344
"astro": "workspace:*",
4445
"astro-scripts": "workspace:*",
45-
"svelte": "^5.38.7"
46+
"cheerio": "1.1.2",
47+
"svelte": "^5.39.3"
4648
},
4749
"peerDependencies": {
4850
"astro": "^5.0.0",

packages/integrations/svelte/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ async function renderToStaticMarkup(
5454
}));
5555
}
5656

57-
const result = render(Component, {
57+
const result = await render(Component, {
5858
props: {
5959
...props,
6060
children,
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import assert from 'node:assert/strict';
2+
import { after, before, describe, it } from 'node:test';
3+
import { load as cheerioLoad } from 'cheerio';
4+
import { loadFixture } from '../../../astro/test/test-utils.js';
5+
6+
let fixture;
7+
8+
describe('Async rendering', () => {
9+
before(async () => {
10+
fixture = await loadFixture({
11+
root: new URL('./fixtures/async-rendering/', import.meta.url),
12+
});
13+
});
14+
15+
describe('build', () => {
16+
before(async () => {
17+
await fixture.build();
18+
});
19+
20+
it('Can render async components', async () => {
21+
const html = await fixture.readFile('/index.html');
22+
const $ = cheerioLoad(html);
23+
24+
assert.ok($('.weather').text().startsWith('The current temperature at KSC is'));
25+
});
26+
});
27+
28+
describe('dev', () => {
29+
/** @type {import('../../../astro/test/test-utils.js').Fixture} */
30+
let devServer;
31+
32+
before(async () => {
33+
devServer = await fixture.startDevServer();
34+
});
35+
36+
after(async () => {
37+
await devServer.stop();
38+
});
39+
40+
it('Can render async components', async () => {
41+
const html = await fixture.fetch('/').then((res) => res.text());
42+
const $ = cheerioLoad(html);
43+
44+
assert.ok($('.weather').text().startsWith('The current temperature at KSC is'));
45+
});
46+
});
47+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// @ts-check
2+
import { defineConfig } from 'astro/config';
3+
4+
import svelte from '@astrojs/svelte';
5+
6+
// https://astro.build/config
7+
export default defineConfig({
8+
integrations: [svelte()]
9+
});
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "async-rendering",
3+
"type": "module",
4+
"version": "0.0.1",
5+
"scripts": {
6+
"dev": "astro dev",
7+
"build": "astro build",
8+
"preview": "astro preview",
9+
"astro": "astro"
10+
},
11+
"dependencies": {
12+
"@astrojs/svelte": "^7.1.1",
13+
"astro": "^5.13.10",
14+
"svelte": "^5.39.3"
15+
}
16+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<script lang="ts">
2+
import type { Snippet } from 'svelte';
3+
4+
interface Props {
5+
children?: Snippet
6+
}
7+
8+
let { children }: Props = $props();
9+
let count = $state(0);
10+
11+
function add() {
12+
count += 1;
13+
}
14+
15+
function subtract() {
16+
count -= 1;
17+
}
18+
</script>
19+
20+
<div class="counter">
21+
<button onclick={subtract}>-</button>
22+
<pre>{count}</pre>
23+
<button onclick={add}>+</button>
24+
</div>
25+
<div class="message">
26+
{@render children?.()}
27+
</div>
28+
29+
<style>
30+
.counter {
31+
display: grid;
32+
font-size: 2em;
33+
grid-template-columns: repeat(3, minmax(0, 1fr));
34+
margin-top: 2em;
35+
place-items: center;
36+
}
37+
38+
.message {
39+
text-align: center;
40+
}
41+
</style>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
const req = await fetch("https://api.open-meteo.com/v1/forecast?latitude=28.52&longitude=-80.68&current=temperature_2m,wind_speed_10m");
3+
const weather = await req.json();
4+
</script>
5+
6+
7+
<p class="weather">The current temperature at KSC is {weather.current.temperature_2m}{weather.current_units.temperature_2m}, with a
8+
wind speed of {weather.current.wind_speed_10m} {weather.current_units.wind_speed_10m}. 🚀
9+
</p>

0 commit comments

Comments
 (0)