diff --git a/aleksis/apps/chronos/frontend/components/Timetable.vue b/aleksis/apps/chronos/frontend/components/Timetable.vue
index 5c0f66811aa7fc88098863b7fb71f53975e272b0..78947059bd524bcd829077d790dec3581bfe42a2 100644
--- a/aleksis/apps/chronos/frontend/components/Timetable.vue
+++ b/aleksis/apps/chronos/frontend/components/Timetable.vue
@@ -1,9 +1,84 @@
 <script setup>
 import TimetableWrapper from "./TimetableWrapper.vue";
 </script>
+
 <script>
+import { DateTime } from "luxon";
+
 export default {
   name: "Timetable",
+  data() {
+    return {
+      calendarFocus: "",
+      calendarType: "week",
+      initialRouteFocusSet: false,
+    };
+  },
+  methods: {
+    setCalendarFocus(val) {
+      this.calendarFocus = val;
+    },
+    setCalendarType(val) {
+      this.calendarType = val;
+    },
+    setInnerFocusAndType() {
+      if (this.$route.name === "chronos.timetableWithId") {
+        this.$refs.calendarWithControls.setCalendarFocus(
+          DateTime.now().toISODate(),
+        );
+        this.$refs.calendarWithControls.setCalendarType(
+          this.$vuetify.breakpoint.mdAndDown ? "day" : "week",
+        );
+      } else {
+        this.initialRouteFocusSet = true;
+        this.$refs.calendarWithControls.setCalendarFocus(
+          [
+            this.$route.params.year,
+            this.$route.params.month,
+            this.$route.params.day,
+          ].join("-"),
+        );
+        this.$refs.calendarWithControls.setCalendarType(
+          this.$route.params.view,
+        );
+      }
+    },
+  },
+  watch: {
+    calendarFocus(newValue, oldValue) {
+      // Do not redirect on first page load
+      if (oldValue === "") return;
+
+      // Do not redirect when calendar focus was just set with route param values
+      if (this.initialRouteFocusSet) {
+        this.initialRouteFocusSet = false;
+        return;
+      }
+
+      const [year, month, day] = newValue.split("-");
+      this.$router.push({
+        name: "chronos.timetableWithIdAndParams",
+        params: {
+          view: this.calendarType,
+          year,
+          month,
+          day,
+        },
+      });
+    },
+    calendarType(newValue) {
+      const [year, month, day] = this.calendarFocus.split("-");
+      this.$router.push({
+        name: "chronos.timetableWithIdAndParams",
+        params: {
+          view: newValue,
+          year,
+          month,
+          day,
+        },
+      });
+    },
+  },
 };
 </script>
 
@@ -17,6 +92,10 @@ export default {
           { name: 'holidays' },
         ]"
         :params="{ type: selected.type, id: selected.objId }"
+        ref="calendarWithControls"
+        @changeCalendarFocus="setCalendarFocus"
+        @changeCalendarType="setCalendarType"
+        @calendarReady="setInnerFocusAndType"
       />
     </template>
   </timetable-wrapper>
diff --git a/aleksis/apps/chronos/frontend/index.js b/aleksis/apps/chronos/frontend/index.js
index 4749ac593afcd244d9d4f06b992ef6dea99c0225..fa829bbfb83b40e3a09ce59f16069be55f89cdd0 100644
--- a/aleksis/apps/chronos/frontend/index.js
+++ b/aleksis/apps/chronos/frontend/index.js
@@ -31,6 +31,17 @@ export default {
         permission: "chronos.view_timetable_overview_rule",
         fullWidth: true,
       },
+      children: [
+        {
+          path: ":view(month|week|day)/:year(\\d\\d\\d\\d)/:month(\\d\\d)/:day(\\d\\d)/",
+          component: Timetable,
+          name: "chronos.timetableWithIdAndParams",
+          meta: {
+            permission: "chronos.view_timetable_overview_rule",
+            fullWidth: true,
+          },
+        },
+      ],
     },
     {
       path: "substitutions/print/",