diff --git a/ts/addons/BUILD.bazel b/ts/addons/BUILD.bazel
new file mode 100644
index 00000000000..4eb92f4b9dc
--- /dev/null
+++ b/ts/addons/BUILD.bazel
@@ -0,0 +1,77 @@
+load("@npm//@bazel/typescript:index.bzl", "ts_library")
+load("//ts/svelte:svelte.bzl", "compile_svelte", "svelte_check")
+load("//ts:prettier.bzl", "prettier_test")
+load("//ts:eslint.bzl", "eslint_test")
+load("//ts:esbuild.bzl", "esbuild")
+
+svelte_files = glob(["*.svelte"])
+
+svelte_names = [f.replace(".svelte", "") for f in svelte_files]
+
+filegroup(
+ name = "svelte_components",
+ srcs = svelte_names,
+ visibility = ["//visibility:public"],
+)
+
+compile_svelte(
+ name = "svelte",
+ srcs = svelte_files,
+ visibility = ["//visibility:public"],
+ deps = [
+ "//ts/sass:button_mixins_lib",
+ "//ts/sass/bootstrap",
+ ],
+)
+
+ts_library(
+ name = "addons",
+ srcs = glob(
+ ["*.ts"],
+ exclude = ["*.test.ts"],
+ ),
+ module_name = "addons",
+ tsconfig = "//ts:tsconfig.json",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//ts/components",
+ "//ts/lib",
+ "//ts/sveltelib",
+ "@npm//@popperjs/core",
+ "@npm//@types/bootstrap",
+ "@npm//bootstrap",
+ "@npm//svelte",
+ ] + svelte_names,
+)
+
+# Tests
+################
+
+prettier_test(
+ name = "format_check",
+ srcs = glob([
+ "*.ts",
+ "*.svelte",
+ ]),
+)
+
+eslint_test(
+ name = "eslint",
+ srcs = glob(
+ [
+ "*.ts",
+ ],
+ ),
+)
+
+svelte_check(
+ name = "svelte_check",
+ srcs = glob([
+ "*.ts",
+ "*.svelte",
+ ]) + [
+ "//ts/sass:button_mixins_lib",
+ "//ts/sass/bootstrap",
+ "@npm//@types/bootstrap",
+ ],
+)
diff --git a/ts/addons/IconButton.svelte b/ts/addons/IconButton.svelte
new file mode 100644
index 00000000000..e955ff78b83
--- /dev/null
+++ b/ts/addons/IconButton.svelte
@@ -0,0 +1,36 @@
+
+
+
+
+
+
diff --git a/ts/addons/Slotted.svelte b/ts/addons/Slotted.svelte
new file mode 100644
index 00000000000..bec833df3f5
--- /dev/null
+++ b/ts/addons/Slotted.svelte
@@ -0,0 +1,12 @@
+
+
+
+
+ {slotted}
+
diff --git a/ts/addons/SlottedHTML.svelte b/ts/addons/SlottedHTML.svelte
new file mode 100644
index 00000000000..45c8fb3f5f5
--- /dev/null
+++ b/ts/addons/SlottedHTML.svelte
@@ -0,0 +1,12 @@
+
+
+
+
+ {@html slotted}
+
diff --git a/ts/addons/WithContext.svelte b/ts/addons/WithContext.svelte
new file mode 100644
index 00000000000..9ac3dd27d74
--- /dev/null
+++ b/ts/addons/WithContext.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+ {#each [getComponent({ context })] as data}
+
+ {/each}
+
diff --git a/ts/addons/WithShortcut.svelte b/ts/addons/WithShortcut.svelte
new file mode 100644
index 00000000000..1beed958836
--- /dev/null
+++ b/ts/addons/WithShortcut.svelte
@@ -0,0 +1,20 @@
+
+
+
+
+ {#each [getComponent({ createShortcut, shortcutLabel })] as data}
+
+ {/each}
+
diff --git a/ts/addons/WithState.svelte b/ts/addons/WithState.svelte
new file mode 100644
index 00000000000..ce5a1f3ac14
--- /dev/null
+++ b/ts/addons/WithState.svelte
@@ -0,0 +1,19 @@
+
+
+
+
+ {#each [getComponent({ state, updateState })] as data}
+
+ {/each}
+
diff --git a/ts/addons/dynamic-components.ts b/ts/addons/dynamic-components.ts
new file mode 100644
index 00000000000..5a082ee8a9b
--- /dev/null
+++ b/ts/addons/dynamic-components.ts
@@ -0,0 +1,25 @@
+// Copyright: Ankitects Pty Ltd and contributors
+// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+
+import { dynamicComponent } from "sveltelib/dynamicComponent";
+
+/* components */
+import IconButton from "./IconButton.svelte";
+export const iconButton = dynamicComponent(IconButton);
+
+/* decorators */
+import WithContext from "./WithContext.svelte";
+export const withContext = dynamicComponent(WithContext);
+
+import WithState from "./WithState.svelte";
+export const withState = dynamicComponent(WithState);
+
+import WithShortcut from "./WithShortcut.svelte";
+export const withShortcut = dynamicComponent(WithShortcut);
+
+/* slots */
+import Slotted from "./Slotted.svelte";
+export const slotted = dynamicComponent(Slotted);
+
+import SlottedHTML from "./SlottedHTML.svelte";
+export const slottedHtml = dynamicComponent(SlottedHTML);
diff --git a/ts/components/IconButton.svelte b/ts/components/IconButton.svelte
index 300a551eb4c..0d1ee32f710 100644
--- a/ts/components/IconButton.svelte
+++ b/ts/components/IconButton.svelte
@@ -31,12 +31,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html