From 8299cbf11b266a561c822a7212526f3fbbadc53e Mon Sep 17 00:00:00 2001
From: Mathias Picker <48158184+MathiasWP@users.noreply.github.com>
Date: Tue, 25 Aug 2026 13:25:40 +0200
Subject: [PATCH] perf: use `$.comment()` for single comment templates (#18714)
Move the "single comment, we can make this faster" optimization into `transform_template` so all usage locations can benefit (e.g. select/option elements)
---------
Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com>
---
.changeset/quiet-swans-invent.md | 5 +++++
.../client/transform-template/index.js | 6 ++++++
.../3-transform/client/visitors/Fragment.js | 9 ++-------
.../_expected/client/index.svelte.js | 19 +++++++++----------
4 files changed, 22 insertions(+), 17 deletions(-)
create mode 100644 .changeset/quiet-swans-invent.md
diff --git a/.changeset/quiet-swans-invent.md b/.changeset/quiet-swans-invent.md
new file mode 100644
index 0000000000..3b35a933d5
--- /dev/null
+++ b/.changeset/quiet-swans-invent.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+perf: use `$.comment()` for single-comment templates
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js
index 5fdc88844b..4c6d5f4ece 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js
@@ -39,6 +39,12 @@ export function transform_template(state, name, flags = 0) {
const namespace = state.metadata.namespace;
const tree = state.options.fragments === 'tree';
+ const { nodes } = state.template;
+ const is_lone_anchor = nodes.length === 1 && nodes[0].type === 'comment';
+
+ // special case - `$.comment` creates the anchor more cheaply than cloning a template
+ if (is_lone_anchor) return b.id('$.comment');
+
const expression = tree ? state.template.as_tree() : state.template.as_html();
const key =
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js
index 893b1db568..1272290c4a 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js
@@ -141,14 +141,9 @@ export function Fragment(node, context) {
flags |= TEMPLATE_USE_IMPORT_NODE;
}
- if (state.template.nodes.length === 1 && state.template.nodes[0].type === 'comment') {
- // special case — we can use `$.comment` instead of creating a unique template
- state.init.unshift(b.var(id, b.call('$.comment')));
- } else {
- const template_name = transform_template(state, 'root', flags);
+ const template_name = transform_template(state, 'root', flags);
- state.init.unshift(b.var(id, b.call(template_name)));
- }
+ state.init.unshift(b.var(id, b.call(template_name)));
close = b.stmt(b.call('$.append', b.id('$$anchor'), id));
}
diff --git a/packages/svelte/tests/snapshot/samples/select-with-rich-content/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/select-with-rich-content/_expected/client/index.svelte.js
index c8354fe67e..fbeecbafb3 100644
--- a/packages/svelte/tests/snapshot/samples/select-with-rich-content/_expected/client/index.svelte.js
+++ b/packages/svelte/tests/snapshot/samples/select-with-rich-content/_expected/client/index.svelte.js
@@ -35,7 +35,6 @@ var option_content = $.from_html(`Rich`, 1);
var root_4 = $.from_html(``);
var root_5 = $.from_html(``);
var root_6 = $.from_html(``);
-var select_content = $.from_html(``, 1);
var option_content_1 = $.from_html(`Bold`, 1);
var option_content_2 = $.from_html(`Italic text`, 1);
var option_content_3 = $.from_html(` `, 1);
@@ -116,7 +115,7 @@ export default function Select_with_rich_content($$anchor) {
$.customizable_select(select_4, () => {
var anchor_1 = $.child(select_4);
- var fragment_2 = select_content();
+ var fragment_2 = $.comment();
var node_2 = $.first_child(fragment_2);
opt(node_2);
@@ -291,7 +290,7 @@ export default function Select_with_rich_content($$anchor) {
$.customizable_select(select_13, () => {
var anchor_6 = $.child(select_13);
- var fragment_8 = select_content();
+ var fragment_8 = $.comment();
var node_7 = $.first_child(fragment_8);
Option(node_7, {});
@@ -302,7 +301,7 @@ export default function Select_with_rich_content($$anchor) {
$.customizable_select(select_14, () => {
var anchor_7 = $.child(select_14);
- var fragment_9 = select_content();
+ var fragment_9 = $.comment();
var node_8 = $.first_child(fragment_9);
option_snippet(node_8);
@@ -313,7 +312,7 @@ export default function Select_with_rich_content($$anchor) {
$.customizable_select(select_15, () => {
var anchor_8 = $.child(select_15);
- var fragment_10 = select_content();
+ var fragment_10 = $.comment();
var node_9 = $.first_child(fragment_10);
$.html(node_9, () => html);
@@ -325,7 +324,7 @@ export default function Select_with_rich_content($$anchor) {
$.customizable_select(optgroup_2, () => {
var anchor_9 = $.child(optgroup_2);
- var fragment_11 = select_content();
+ var fragment_11 = $.comment();
var node_10 = $.first_child(fragment_11);
Option(node_10, {});
@@ -339,7 +338,7 @@ export default function Select_with_rich_content($$anchor) {
$.customizable_select(optgroup_3, () => {
var anchor_10 = $.child(optgroup_3);
- var fragment_12 = select_content();
+ var fragment_12 = $.comment();
var node_11 = $.first_child(fragment_12);
option_snippet2(node_11);
@@ -353,7 +352,7 @@ export default function Select_with_rich_content($$anchor) {
$.customizable_select(option_16, () => {
var anchor_11 = $.child(option_16);
- var fragment_13 = select_content();
+ var fragment_13 = $.comment();
var node_12 = $.first_child(fragment_13);
$.html(node_12, () => 'Bold HTML');
@@ -366,7 +365,7 @@ export default function Select_with_rich_content($$anchor) {
$.customizable_select(select_19, () => {
var anchor_12 = $.child(select_19);
- var fragment_14 = select_content();
+ var fragment_14 = $.comment();
var node_13 = $.first_child(fragment_14);
$.each(node_13, 1, () => items, $.index, ($$anchor, item) => {
@@ -380,7 +379,7 @@ export default function Select_with_rich_content($$anchor) {
$.customizable_select(select_20, () => {
var anchor_13 = $.child(select_20);
- var fragment_16 = select_content();
+ var fragment_16 = $.comment();
var node_14 = $.first_child(fragment_16);
{