diff --git a/.changeset/swift-poets-carry.md b/.changeset/swift-poets-carry.md
new file mode 100644
index 0000000000..0cb65a9389
--- /dev/null
+++ b/.changeset/swift-poets-carry.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+feat: faster HTML tags
diff --git a/packages/svelte/src/internal/client/dom/blocks/html.js b/packages/svelte/src/internal/client/dom/blocks/html.js
index 31fcc3d2e2..ff96617885 100644
--- a/packages/svelte/src/internal/client/dom/blocks/html.js
+++ b/packages/svelte/src/internal/client/dom/blocks/html.js
@@ -1,7 +1,8 @@
import { derived } from '../../reactivity/deriveds.js';
import { render_effect } from '../../reactivity/effects.js';
import { get } from '../../runtime.js';
-import { reconcile_html, remove } from '../reconciler.js';
+import { hydrate_nodes, hydrating } from '../hydration.js';
+import { create_fragment_from_html, remove } from '../reconciler.js';
/**
* @param {Element | Text | Comment} anchor
@@ -13,10 +14,53 @@ export function html(anchor, get_value, svg) {
let value = derived(get_value);
render_effect(() => {
- var dom = reconcile_html(anchor, get(value), svg);
+ var dom = html_to_dom(anchor, get(value), svg);
if (dom) {
return () => remove(dom);
}
});
}
+
+/**
+ * Creates the content for a `@html` tag from its string value,
+ * inserts it before the target anchor and returns the new nodes.
+ * @template V
+ * @param {Element | Text | Comment} target
+ * @param {V} value
+ * @param {boolean} svg
+ * @returns {Element | Comment | (Element | Comment | Text)[]}
+ */
+function html_to_dom(target, value, svg) {
+ if (hydrating) return hydrate_nodes;
+
+ var html = value + '';
+ if (svg) html = ``;
+
+ // Don't use create_fragment_with_script_from_html here because that would mean script tags are executed.
+ // @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons.
+ /** @type {DocumentFragment | Element} */
+ var node = create_fragment_from_html(html);
+
+ if (svg) {
+ node = /** @type {Element} */ (node.firstChild);
+ }
+
+ if (node.childNodes.length === 1) {
+ var child = /** @type {Text | Element | Comment} */ (node.firstChild);
+ target.before(child);
+ return child;
+ }
+
+ var nodes = /** @type {Array} */ ([...node.childNodes]);
+
+ if (svg) {
+ while (node.firstChild) {
+ target.before(node.firstChild);
+ }
+ } else {
+ target.before(node);
+ }
+
+ return nodes;
+}
diff --git a/packages/svelte/src/internal/client/dom/reconciler.js b/packages/svelte/src/internal/client/dom/reconciler.js
index 18fac51f96..5b9f246ed3 100644
--- a/packages/svelte/src/internal/client/dom/reconciler.js
+++ b/packages/svelte/src/internal/client/dom/reconciler.js
@@ -1,4 +1,3 @@
-import { hydrate_nodes, hydrating } from './hydration.js';
import { is_array } from '../utils.js';
/** @param {string} html */
@@ -23,38 +22,3 @@ export function remove(current) {
current.remove();
}
}
-
-/**
- * Creates the content for a `@html` tag from its string value,
- * inserts it before the target anchor and returns the new nodes.
- * @template V
- * @param {Element | Text | Comment} target
- * @param {V} value
- * @param {boolean} svg
- * @returns {Element | Comment | (Element | Comment | Text)[]}
- */
-export function reconcile_html(target, value, svg) {
- if (hydrating) {
- return hydrate_nodes;
- }
- var html = value + '';
- // Even if html is the empty string we need to continue to insert something or
- // else the element ordering gets out of sync, resulting in subsequent values
- // not getting inserted anymore.
- var frag_nodes;
- if (svg) {
- html = ``;
- }
- // Don't use create_fragment_with_script_from_html here because that would mean script tags are executed.
- // @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons.
- var content = create_fragment_from_html(html);
- if (svg) {
- content = /** @type {DocumentFragment} */ (/** @type {unknown} */ (content.firstChild));
- }
- var clone = content.cloneNode(true);
- frag_nodes = [...clone.childNodes];
- frag_nodes.forEach((node) => {
- target.before(node);
- });
- return /** @type {Array} */ (frag_nodes);
-}