diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 42d851f79bfa51e26f8cb5a97f101d02c207c47e..58be4f5ac47f1f0a92e5354440d357f3ec84f837 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,9 +6,22 @@ All notable changes to this project will be documented in this file.
 The format is based on `Keep a Changelog`_,
 and this project adheres to `Semantic Versioning`_.
 
+Breaking changes
+----------------
+
+Removed
+~~~~~~~
+
+* Remove legacy menu entries.
+
 Unreleased
 ----------
 
+Added
+~~~~~
+
+* Support for usage with new AlekSIS SPA.
+
 `1.0.2`_ - 2022-11-04
 ---------------------
 
diff --git a/aleksis/apps/stoelindeling/frontend/index.js b/aleksis/apps/stoelindeling/frontend/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..bb045a37152fa13fe8faaa142f6b6d79dbbd1a93
--- /dev/null
+++ b/aleksis/apps/stoelindeling/frontend/index.js
@@ -0,0 +1,68 @@
+export default
+  {
+    meta: {
+      inMenu: true,
+      titleKey: "stoelindeling.menu_title",
+      icon: "mdi-view-list-outline",
+    },
+    props: {
+      byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+    },
+    children: [
+      {
+        path: "seating_plans/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "stoelindeling.seatingPlans",
+        meta: {
+          inMenu: true,
+          titleKey: "stoelindeling.menu_title",
+          icon: "mdi-view-list-outline",
+          permission: "stoelindeling.view_seatingplans_rule",
+        },
+        props: {
+          byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+        },
+      },
+      {
+        path: "seating_plans/create/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "stoelindeling.createSeatingPlan",
+        props: {
+          byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+        },
+      },
+      {
+        path: "seating_plans/:pk/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "stoelindeling.seatingPlan",
+        props: {
+          byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+        },
+      },
+      {
+        path: "seating_plans/:pk/edit/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "stoelindeling.editSeatingPlan",
+        props: {
+          byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+        },
+      },
+      {
+        path: "seating_plans/:pk/copy/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "stoelindeling.copySeatingPlan",
+        props: {
+          byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+        },
+      },
+      {
+        path: "seating_plans/:pk/delete/",
+        component: () => import("aleksis.core/components/LegacyBaseTemplate.vue"),
+        name: "stoelindeling.deleteSeatingPlan",
+        props: {
+          byTheGreatnessOfTheAlmightyAleksolotlISwearIAmWorthyOfUsingTheLegacyBaseTemplate: true,
+        },
+      },
+    ],
+  }
+
diff --git a/aleksis/apps/stoelindeling/frontend/messages/de.json b/aleksis/apps/stoelindeling/frontend/messages/de.json
new file mode 100644
index 0000000000000000000000000000000000000000..4d4542c438bbc95914e3371e7219bbdc6f8580d2
--- /dev/null
+++ b/aleksis/apps/stoelindeling/frontend/messages/de.json
@@ -0,0 +1,6 @@
+{
+  "stoelindeling": {
+    "menu_title": "Sitzpläne"
+  }
+}
+
diff --git a/aleksis/apps/stoelindeling/frontend/messages/en.json b/aleksis/apps/stoelindeling/frontend/messages/en.json
new file mode 100644
index 0000000000000000000000000000000000000000..a95f9cc2015a4239ad6a880ad04ca623f220763f
--- /dev/null
+++ b/aleksis/apps/stoelindeling/frontend/messages/en.json
@@ -0,0 +1,6 @@
+{
+  "stoelindeling": {
+    "menu_title": "Seating plans"
+  }
+}
+
diff --git a/aleksis/apps/stoelindeling/menus.py b/aleksis/apps/stoelindeling/menus.py
deleted file mode 100644
index fc2ce01268d08a8463936c232dd08a7afbcd5dbf..0000000000000000000000000000000000000000
--- a/aleksis/apps/stoelindeling/menus.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from django.utils.translation import gettext_lazy as _
-
-MENUS = {
-    "NAV_MENU_CORE": [
-        {
-            "name": _("Seating plans"),
-            "url": "seating_plans",
-            "svg_icon": "mdi:view-list-outline",
-            "validators": [
-                (
-                    "aleksis.core.util.predicates.permission_validator",
-                    "stoelindeling.view_seatingplans_rule",
-                ),
-            ],
-        },
-    ]
-}
diff --git a/aleksis/apps/stoelindeling/views.py b/aleksis/apps/stoelindeling/views.py
index 3c7027803fbdcaf597882f63d335d42792a98e37..72ac1682e215a51fe909cb31b5ee51a6a0c2f1d1 100644
--- a/aleksis/apps/stoelindeling/views.py
+++ b/aleksis/apps/stoelindeling/views.py
@@ -10,6 +10,7 @@ from django_tables2 import SingleTableView
 from reversion.views import RevisionMixin
 from rules.contrib.views import PermissionRequiredMixin
 
+from aleksis.core.decorators import pwa_cache
 from aleksis.core.mixins import (
     AdvancedCreateView,
     AdvancedDeleteView,
@@ -24,6 +25,7 @@ from .tables import SeatingPlanTable
 from .util.perms import get_allowed_seating_plans
 
 
+@method_decorator(pwa_cache, name="dispatch")
 class SeatingPlanListView(PermissionRequiredMixin, SingleTableView):
     """Table of all seating plans."""
 
@@ -36,6 +38,7 @@ class SeatingPlanListView(PermissionRequiredMixin, SingleTableView):
         return get_allowed_seating_plans(self.request.user)
 
 
+@method_decorator(pwa_cache, name="dispatch")
 class SeatingPlanDetailView(PermissionRequiredMixin, DetailView):
     """Detail view for seating plans."""
 
diff --git a/docs/conf.py b/docs/conf.py
index eca0873da774bf2150382429afc0dbc7d5cfdd9c..dd57a50e4c74dc7e526c431886d9940e694facfc 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -29,9 +29,9 @@ copyright = "2018-2022 The AlekSIS team"
 author = "The AlekSIS Team"
 
 # The short X.Y version
-version = "1.0"
+version = "2.0"
 # The full version, including alpha/beta/rc tags
-release = "1.0.3.dev0"
+release = "2.0.0.dev0"
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/pyproject.toml b/pyproject.toml
index 499be9f72af3ef933616b4e063d57bf773bdd59f..f8b75e6bbc6b525298cf864bc67c6613ebaabfa3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "AlekSIS-App-Stoelindeling"
-version = "1.0.3.dev0"
+version = "2.0.dev0"
 packages = [
     { include = "aleksis" }
 ]
@@ -30,8 +30,8 @@ secondary = true
 
 [tool.poetry.dependencies]
 python = "^3.9"
-aleksis-core = "^2.8"
-aleksis-app-chronos = "^2.0"
+aleksis-core = "^3.0.dev3"
+aleksis-app-chronos = "^3.0.dev1"
 
 [tool.poetry.dev-dependencies]
 aleksis-builddeps = "*"