diff --git a/src/pages/404.tsx b/src/pages/404.tsx
index 23f20a3a5179d7582cdcfa6894ab344c131e7c4f..73386e65f66884649b28e2acf047eaadfba5a9e2 100644
--- a/src/pages/404.tsx
+++ b/src/pages/404.tsx
@@ -1,4 +1,4 @@
-import { Four04 } from "@/videoag/error/ErrorDisplay";
+import { Four04 } from "@/videoag/error";
 
 export default function Four04Page() {
     return <Four04 />;
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index 86505ff4f5a57489ef7bd549505c67b96ba90e5e..2ecfa31bbbb6a0f2dd533b411af4d37921eec0d1 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -2,15 +2,13 @@ import type { AppProps } from "next/app";
 import { ToastContainer } from "react-toastify";
 import Head from "next/head";
 
-import { AuthStatusProvider } from "@/videoag/authentication/AuthStatus";
-import { showErrorToast } from "@/videoag/error/ErrorDisplay";
-import { LanguageProvider } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { ThemeProvider } from "@/videoag/miscellaneous/Theme";
-import Title from "@/videoag/miscellaneous/TitleComponent";
-import { RealBackendProvider } from "@/videoag/api/ApiProvider";
-import { EditModeProvider } from "@/videoag/object_management/EditModeProvider";
-import DefaultLayout from "@/videoag/site/DefaultLayout";
+import { RealBackendProvider } from "@/videoag/api";
+import { AuthStatusProvider } from "@/videoag/authentication";
+import { showErrorToast } from "@/videoag/error";
+import { LanguageProvider } from "@/videoag/localization";
+import { ReloadBoundary, ThemeProvider, Title } from "@/videoag/miscellaneous";
+import { EditModeProvider } from "@/videoag/object_management";
+import { DefaultLayout } from "@/videoag/site";
 import "@/styles/globals.scss";
 
 export default function App({ Component, pageProps }: AppProps) {
diff --git a/src/pages/courses.tsx b/src/pages/courses.tsx
index ffa6b8d1ff71f89b62b57dd9eb65f13c7a68a9da..0908efedfe8965fd8227d27e807eec75a353e03b 100644
--- a/src/pages/courses.tsx
+++ b/src/pages/courses.tsx
@@ -4,20 +4,17 @@ import DropdownButton from "react-bootstrap/DropdownButton";
 import DropdownItem from "react-bootstrap/DropdownItem";
 
 import type { GetCoursesResponse, course } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { semesterToHuman } from "@/videoag/miscellaneous/Formatting";
-import { UpdateOverlay } from "@/videoag/miscellaneous/UpdateOverlay";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { ErrorComponent, FallbackErrorBoundary } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { ReloadBoundary, semesterToHuman, UpdateOverlay } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMCreate,
     OMEdit,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
+} from "@/videoag/object_management";
 
 type GroupByTypes = "semester" | "full_name" | "organizer" | "topic";
 
diff --git a/src/pages/dynamic_course_listing.tsx b/src/pages/dynamic_course_listing.tsx
index d95a3ed47082714a9a5f82d8b08820524c7d9dda..bad62fd2cc74c2612ad11bdb56fb5f4df33951ed 100644
--- a/src/pages/dynamic_course_listing.tsx
+++ b/src/pages/dynamic_course_listing.tsx
@@ -2,12 +2,11 @@ import { useEffect, useRef, useState, useTransition } from "react";
 import { usePathname } from "next/navigation";
 
 import { GetCourseResponse } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import CourseListing from "@/videoag/course/CourseListing";
-import { Four04, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { CourseListing } from "@/videoag/course";
+import { Four04, ErrorComponent, FallbackErrorBoundary } from "@/videoag/error";
+import { ReloadBoundary } from "@/videoag/miscellaneous";
 
 function ActualRedirector() {
     const api = useApi();
diff --git a/src/pages/dynamic_player.tsx b/src/pages/dynamic_player.tsx
index 89946e4bba36022d33e320228b76ca46477759f1..4c98c8a0efbc3a18930c5ca9b855b6a0059d4980 100644
--- a/src/pages/dynamic_player.tsx
+++ b/src/pages/dynamic_player.tsx
@@ -2,13 +2,11 @@ import { useEffect, useRef, useState, useTransition } from "react";
 import { usePathname } from "next/navigation";
 
 import { course, lecture } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { Backend } from "@/videoag/api/Backend";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import Player, { EmbeddedPlayer } from "@/videoag/course/Player";
-import { Four04, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi, Backend } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { Player, EmbeddedPlayer } from "@/videoag/course";
+import { Four04, ErrorComponent, FallbackErrorBoundary } from "@/videoag/error";
+import { ReloadBoundary } from "@/videoag/miscellaneous";
 
 export interface PlayerData {
     course: course;
diff --git a/src/pages/feedback.tsx b/src/pages/feedback.tsx
index 60cf67ea6d977ab5e8334d1477bff6cffebde303..7c72adee2b503aad2087d196a163cc9a8fab76f4 100644
--- a/src/pages/feedback.tsx
+++ b/src/pages/feedback.tsx
@@ -2,10 +2,10 @@ import type React from "react";
 import { useState } from "react";
 import Link from "next/link";
 
-import { useApi } from "@/videoag/api/ApiProvider";
-import { showError } from "@/videoag/error/ErrorDisplay";
-import { StringEditor, BooleanEditor } from "@/videoag/form/TypeEditor";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
+import { useApi } from "@/videoag/api";
+import { showError } from "@/videoag/error";
+import { StringEditor, BooleanEditor } from "@/videoag/form";
+import { useLanguage } from "@/videoag/localization";
 
 export default function Feedback() {
     const api = useApi();
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index f1451cdf132c74921ee2ca2e763ef7d22044caa2..bfc6eed86c5f3a28143baebbe6baae9bec8b108d 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -4,28 +4,32 @@ import { useSearchParams } from "next/navigation";
 import { useRouter } from "next/router";
 
 import type { GetHomepageResponse, featured, int } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { LectureCard } from "@/videoag/course/Lecture";
-import { LectureLiveLabel } from "@/videoag/course/LiveLabel";
-import { ErrorComponent, showError, showWarningToast } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { LectureCard, LectureLiveLabel } from "@/videoag/course";
+import {
+    ErrorComponent,
+    showError,
+    showWarningToast,
+    FallbackErrorBoundary,
+} from "@/videoag/error";
 import { useLanguage } from "@/videoag/localization/LanguageProvider";
 import {
     datetimeToStringOnlyDate,
     datetimeToStringOnlyTime,
-} from "@/videoag/miscellaneous/Formatting";
-import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { useDebounceWithArgument } from "@/videoag/miscellaneous/PromiseHelpers";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+    ReloadBoundary,
+    useReloadBoundary,
+    useDebounceWithArgument,
+} from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMCreate,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
-import { Search } from "@/videoag/site/DefaultLayout";
+} from "@/videoag/object_management";
+import { Search } from "@/videoag/site";
 import { basePath } from "@/../basepath";
 
 import { SearchResults } from "./search";
diff --git a/src/pages/internal/changelog.tsx b/src/pages/internal/changelog.tsx
index 8f1515773954951750615f117a9282d922ef30ea..68ae0f68d05d4cdb372feb050b8b34cf4e623c2d 100644
--- a/src/pages/internal/changelog.tsx
+++ b/src/pages/internal/changelog.tsx
@@ -2,17 +2,12 @@ import { useEffect, useState } from "react";
 import { DateTime } from "luxon";
 
 import type { changelog_entry, GetChangelogResponse, int, object_type } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { OBJECT_TYPES } from "@/videoag/api/Backend";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { showError, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { ChooserEditor, StringEditor, IntEditor } from "@/videoag/form/TypeEditor";
-import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import {
-    useFilteredDataView,
-    PagingNavigation,
-    FilterInput,
-} from "@/videoag/object_management/FilteredData";
+import { useApi, OBJECT_TYPES } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { showError, ErrorComponent } from "@/videoag/error";
+import { ChooserEditor, StringEditor, IntEditor } from "@/videoag/form";
+import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous";
+import { useFilteredDataView, PagingNavigation, FilterInput } from "@/videoag/object_management";
 
 function ValToStr({ val }: { val: any }) {
     const [expand, setExpand] = useState(false);
diff --git a/src/pages/internal/import.tsx b/src/pages/internal/import.tsx
index aa9cfd0eba13b9d6f5e92162007326a232a5b51b..ba7cf29ad35e9fe5fa6ad2e9b2c907ecdd77c6e1 100644
--- a/src/pages/internal/import.tsx
+++ b/src/pages/internal/import.tsx
@@ -4,11 +4,10 @@ import { useSearchParams } from "next/navigation";
 import { DateTime } from "luxon";
 
 import type { GetNewConfigurationResponse, course } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { showError, showErrorToast } from "@/videoag/error/ErrorDisplay";
-import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { ICalEvent, parseICal } from "@/videoag/miscellaneous/Calendar";
+import { useApi } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { showError, showErrorToast } from "@/videoag/error";
+import { ICalEvent, parseICal, ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous";
 
 function TerminBody({
     course,
diff --git a/src/pages/internal/jobs.tsx b/src/pages/internal/jobs.tsx
index 357bb04682533543bb15f36baf4d5aaea7c552a0..aa317459a68a21c5c64f8258fa5516e58e20f904 100644
--- a/src/pages/internal/jobs.tsx
+++ b/src/pages/internal/jobs.tsx
@@ -1,18 +1,15 @@
 import type { int, job_state, GetJobsResponse } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { JOB_STATES } from "@/videoag/api/Backend";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { StringEditor, ChooserEditor } from "@/videoag/form/TypeEditor";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { ExpandableString } from "@/videoag/miscellaneous/ExpandableString";
-import { datetimeToString } from "@/videoag/miscellaneous/Formatting";
-import { Spinner } from "@/videoag/miscellaneous/Util";
+import { useApi, JOB_STATES } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { ErrorComponent } from "@/videoag/error";
+import { StringEditor, ChooserEditor } from "@/videoag/form";
 import {
-    useFilteredDataView,
-    PagingNavigation,
-    FilterInput,
-} from "@/videoag/object_management/FilteredData";
+    ReloadBoundary,
+    ExpandableString,
+    datetimeToString,
+    Spinner,
+} from "@/videoag/miscellaneous";
+import { useFilteredDataView, PagingNavigation, FilterInput } from "@/videoag/object_management";
 
 function JobList({ jobsResp }: { jobsResp: GetJobsResponse }) {
     return (
diff --git a/src/pages/internal/sorter_files.tsx b/src/pages/internal/sorter_files.tsx
index 4d9aa96bca9a1b085b3779fc7bc2dd3974da84c3..2b6044d7ba100b0366204e6d132c01058862f5fc 100644
--- a/src/pages/internal/sorter_files.tsx
+++ b/src/pages/internal/sorter_files.tsx
@@ -2,21 +2,23 @@ import { useState } from "react";
 import Link from "next/link";
 
 import type { int, GetSorterFilesResponse } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { showError, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { BooleanEditor } from "@/videoag/form/TypeEditor";
-import { JobStatusCard } from "@/videoag/job/Job";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { ExpandableString } from "@/videoag/miscellaneous/ExpandableString";
-import { datetimeToString } from "@/videoag/miscellaneous/Formatting";
-import { showInfoToast } from "@/videoag/miscellaneous/Util";
+import { useApi } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { showError, ErrorComponent } from "@/videoag/error";
+import { BooleanEditor } from "@/videoag/form";
+import { JobStatusCard } from "@/videoag/job";
+import {
+    ReloadBoundary,
+    ExpandableString,
+    datetimeToString,
+    showInfoToast,
+} from "@/videoag/miscellaneous";
 import {
     useFilteredDataView,
     PagingNavigation,
     FilterInput,
-} from "@/videoag/object_management/FilteredData";
-import { EmbeddedOMFieldComponent } from "@/videoag/object_management/OMConfigComponent";
+    EmbeddedOMFieldComponent,
+} from "@/videoag/object_management";
 
 function SorterFileList({ sorterFilesResp }: { sorterFilesResp: GetSorterFilesResponse }) {
     return (
diff --git a/src/pages/internal/timetable.tsx b/src/pages/internal/timetable.tsx
index a3146878ef599bf436adffe04648ba1cbf674a95..947303976f8d8b2951c5f618629818f90e3e9987 100644
--- a/src/pages/internal/timetable.tsx
+++ b/src/pages/internal/timetable.tsx
@@ -1,7 +1,7 @@
 import Link from "next/link";
 import { DateTime } from "luxon";
 
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
+import { ModeratorBarrier } from "@/videoag/authentication";
 
 interface Days {
     index: number;
diff --git a/src/pages/internal/user.tsx b/src/pages/internal/user.tsx
index 2233cceb3e895ac7b645d966cc153b8abdea7559..7ed93cf825e59ee7bc7d7a2ee2092e11daf4c8be 100644
--- a/src/pages/internal/user.tsx
+++ b/src/pages/internal/user.tsx
@@ -2,11 +2,11 @@ import type React from "react";
 import { useEffect, useState } from "react";
 
 import type { GetMySettingsResponse, settings_entry, settings_section } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import ModeratorBarrier from "@/videoag/authentication/ModeratorBarrier";
-import { showError } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi } from "@/videoag/api";
+import { ModeratorBarrier } from "@/videoag/authentication";
+import { showError } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { ReloadBoundary, useReloadBoundary } from "@/videoag/miscellaneous";
 
 function BoolEntry({ entry, disabled }: { entry: settings_entry; disabled: boolean }) {
     const reloadFunc = useReloadBoundary();
diff --git a/src/pages/search.tsx b/src/pages/search.tsx
index 8345e466918cb2e1f0442095d275ad449a38bf1c..80e17ab69650f9fa4c9013811f40568de4a7f304 100644
--- a/src/pages/search.tsx
+++ b/src/pages/search.tsx
@@ -4,12 +4,11 @@ import { useSearchParams } from "next/navigation";
 import { useRouter } from "next/router";
 
 import type { SearchResponse, course, lecture } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { LectureListItem } from "@/videoag/course/Lecture";
-import { ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { useDebounce } from "@/videoag/miscellaneous/PromiseHelpers";
+import { useApi } from "@/videoag/api";
+import { LectureListItem } from "@/videoag/course";
+import { ErrorComponent } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { ReloadBoundary, useDebounce } from "@/videoag/miscellaneous";
 
 import { CourseItem } from "./courses";
 
diff --git a/src/videoag/api/index.ts b/src/videoag/api/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ed895ce50b61cc09a2da66e04d5e613afe855e9
--- /dev/null
+++ b/src/videoag/api/index.ts
@@ -0,0 +1,3 @@
+export { ApiError } from "./ApiError";
+export { RealBackendProvider, useApi } from "./ApiProvider";
+export { JOB_STATES, OBJECT_TYPES, type Backend } from "./Backend";
diff --git a/src/videoag/authentication/AuthStatus.tsx b/src/videoag/authentication/AuthStatus.tsx
index d2ab43cce0b28d0bfaaae9d3e836c8c46694ac51..ae297576f4778759e10723b37a4e2dbbacf33a08 100644
--- a/src/videoag/authentication/AuthStatus.tsx
+++ b/src/videoag/authentication/AuthStatus.tsx
@@ -2,7 +2,7 @@ import type React from "react";
 import { createContext, useContext, useState, useEffect, startTransition } from "react";
 
 import { int, user } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
+import { useApi } from "@/videoag/api";
 
 interface AuthStatusData {
     user: user | null;
diff --git a/src/videoag/authentication/ModeratorBarrier.tsx b/src/videoag/authentication/ModeratorBarrier.tsx
index 83435b8428b3c2ae5ee4aa923791ba66b7a014a1..a1000851e0e2f609b019ccd965911751bc5ca8ba 100644
--- a/src/videoag/authentication/ModeratorBarrier.tsx
+++ b/src/videoag/authentication/ModeratorBarrier.tsx
@@ -1,8 +1,8 @@
-import { Four04 } from "@/videoag/error/ErrorDisplay";
-import { useAuthStatus } from "./AuthStatus";
-
 import type React from "react";
 
+import { Four04 } from "@/videoag/error";
+import { useAuthStatus } from "./AuthStatus";
+
 export default function ModeratorBarrier({ children }: { children: React.ReactNode }) {
     const authStatus = useAuthStatus();
 
diff --git a/src/videoag/authentication/UserField.tsx b/src/videoag/authentication/UserField.tsx
index e4206bc66f60ed36ab6dac05879bba50fd2cd2a4..518839ac5b3d02b8a1b7bd64243b18e7f11fde04 100644
--- a/src/videoag/authentication/UserField.tsx
+++ b/src/videoag/authentication/UserField.tsx
@@ -3,13 +3,12 @@ import type React from "react";
 import { useState } from "react";
 import { Dropdown, Popover, OverlayTrigger } from "react-bootstrap";
 
-import { useApi } from "@/videoag/api/ApiProvider";
-import { ApiError } from "@/videoag/api/ApiError";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { showError } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { TooltipButton } from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useApi, ApiError } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { showError } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { TooltipButton } from "@/videoag/miscellaneous";
+import { useEditMode } from "@/videoag/object_management";
 
 export default function UserField({ isUnavailable }: { isUnavailable: boolean }) {
     const api = useApi();
diff --git a/src/videoag/authentication/ViewPermissions.tsx b/src/videoag/authentication/ViewPermissions.tsx
index bd1cbac9b37c0e2a72821c576134f1a0e6572d99..a6664371c8db930b87bd5ac734158124b73bb5c8 100644
--- a/src/videoag/authentication/ViewPermissions.tsx
+++ b/src/videoag/authentication/ViewPermissions.tsx
@@ -2,14 +2,11 @@ import type React from "react";
 import { useEffect, useRef, useState } from "react";
 
 import { course, lecture } from "@/videoag/api/types";
-import { ApiError } from "@/videoag/api/ApiError";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { showError, showErrorToast } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { useDebouncedCall } from "@/videoag/miscellaneous/PromiseHelpers";
+import { ApiError, useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { showError, showErrorToast } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { useReloadBoundary, StylizedText, useDebouncedCall } from "@/videoag/miscellaneous";
 
 export function AuthenticationMethodIcons({
     authentication_methods,
diff --git a/src/videoag/authentication/index.ts b/src/videoag/authentication/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f2b34abe848d9ad5b0ccdb32cd7be855db538052
--- /dev/null
+++ b/src/videoag/authentication/index.ts
@@ -0,0 +1,4 @@
+export { AuthStatus, AuthStatusProvider, useAuthStatus } from "./AuthStatus";
+export { default as ModeratorBarrier } from "./ModeratorBarrier";
+export { default as UserField } from "./UserField";
+export { AuthenticationMethodIcons, ViewPermissionAuthorization } from "./ViewPermissions";
diff --git a/src/videoag/course/Chapter.tsx b/src/videoag/course/Chapter.tsx
index e397aa2426ac7ed87cb4c1bce26c1664afb27bd9..0b54c365c48b4678679d113d3ce2d2b917d7d482 100644
--- a/src/videoag/course/Chapter.tsx
+++ b/src/videoag/course/Chapter.tsx
@@ -3,21 +3,19 @@ import { useEffect, useRef, useState } from "react";
 import { Popover, OverlayTrigger } from "react-bootstrap";
 
 import { int, chapter } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { showError } from "@/videoag/error/ErrorDisplay";
-import { StringEditor } from "@/videoag/form/TypeEditor";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { timestampToString } from "@/videoag/miscellaneous/Formatting";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { showInfoToast } from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus } from "@/videoag/authentication";
+import { showError } from "@/videoag/error";
+import { StringEditor } from "@/videoag/form";
+import { useLanguage } from "@/videoag/localization";
+import { timestampToString, useReloadBoundary, showInfoToast } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
+} from "@/videoag/object_management";
 
 export function Chapters({
     chapters,
diff --git a/src/videoag/course/CourseListing.tsx b/src/videoag/course/CourseListing.tsx
index 3488d348964e9f68477c35e33e0446c209ca52a3..a8247c88eee1b0436635acd0b5df0ad326751ef1 100644
--- a/src/videoag/course/CourseListing.tsx
+++ b/src/videoag/course/CourseListing.tsx
@@ -1,19 +1,17 @@
 import Link from "next/link";
 
 import type { GetCourseResponse } from "@/videoag/api/types";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import { AuthenticationMethodIcons } from "@/videoag/authentication/ViewPermissions";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import Title from "@/videoag/miscellaneous/TitleComponent";
-import { UpdateOverlay } from "@/videoag/miscellaneous/UpdateOverlay";
+import { useAuthStatus, AuthenticationMethodIcons } from "@/videoag/authentication";
+import { useLanguage } from "@/videoag/localization";
+import { Title, UpdateOverlay } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMCreate,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+} from "@/videoag/object_management";
 
 import { LectureListItem } from "./Lecture";
 import DownloadAllModal from "./DownloadManager";
diff --git a/src/videoag/course/DownloadManager.tsx b/src/videoag/course/DownloadManager.tsx
index 1da161676b292f0967b24122905808d4520dc06c..4df2b5bf15c8c5331b23ff8774f3c095a425a859 100644
--- a/src/videoag/course/DownloadManager.tsx
+++ b/src/videoag/course/DownloadManager.tsx
@@ -4,10 +4,9 @@ import DropdownButton from "react-bootstrap/DropdownButton";
 import DropdownItem from "react-bootstrap/DropdownItem";
 
 import type { int, course, lecture } from "@/videoag/api/types";
-import { showWarningToast, showError } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { filesizeToHuman } from "@/videoag/miscellaneous/Formatting";
+import { showWarningToast, showError } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { StylizedText, filesizeToHuman } from "@/videoag/miscellaneous";
 
 import {
     getNamedPublishMedia,
diff --git a/src/videoag/course/Lecture.tsx b/src/videoag/course/Lecture.tsx
index 368b3492ede64a2adf154701ec784d6a9a1d0a34..13125ee083e5c3f50a466ad44ab2d8c86bf8289e 100644
--- a/src/videoag/course/Lecture.tsx
+++ b/src/videoag/course/Lecture.tsx
@@ -2,17 +2,16 @@ import Link from "next/link";
 import { OverlayTrigger, Tooltip } from "react-bootstrap";
 
 import type { lecture, course } from "@/videoag/api/types";
+import { AuthenticationMethodIcons } from "@/videoag/authentication";
+import { datetimeToString, StylizedText } from "@/videoag/miscellaneous";
+import { useLanguage } from "@/videoag/localization";
 import {
+    useEditMode,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
-import { AuthenticationMethodIcons } from "@/videoag/authentication/ViewPermissions";
-import { datetimeToString } from "@/videoag/miscellaneous/Formatting";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
+} from "@/videoag/object_management";
 
 import {
     PublishMediumDownloadButton,
diff --git a/src/videoag/course/LiveLabel.tsx b/src/videoag/course/LiveLabel.tsx
index 816c1d3465935ba672f675a52a3068b9566a10a9..9965e808f80036220edd83c9f546de7d879118ea 100644
--- a/src/videoag/course/LiveLabel.tsx
+++ b/src/videoag/course/LiveLabel.tsx
@@ -1,7 +1,7 @@
 import { DateTime } from "luxon";
 
 import { lecture } from "@/videoag/api/types";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
+import { useLanguage } from "@/videoag/localization";
 
 export function LiveLabel({ nowlive }: { nowlive: boolean }) {
     const { language } = useLanguage();
diff --git a/src/videoag/course/Medium.tsx b/src/videoag/course/Medium.tsx
index c2dadb97fc22db351b9c38f7a5727107f8328e21..2082c43e218ac6d3a4e9d2d6c4687bb6accb3bfe 100644
--- a/src/videoag/course/Medium.tsx
+++ b/src/videoag/course/Medium.tsx
@@ -7,26 +7,23 @@ import {
     publish_medium,
     GetLectureMediaProcessOverviewResponse,
 } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { showError, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { JobStatusCard } from "@/videoag/job/Job";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import {
-    filesizeToHuman,
-    timestampToString,
-    datetimeToString,
-} from "@/videoag/miscellaneous/Formatting";
-import { useMutexCall } from "@/videoag/miscellaneous/PromiseHelpers";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi } from "@/videoag/api";
+import { showError, ErrorComponent } from "@/videoag/error";
+import { JobStatusCard } from "@/videoag/job";
+import { useLanguage } from "@/videoag/localization";
 import {
     Spinner,
+    ReloadBoundary,
+    useMutexCall,
     showInfoToast,
     TooltipButton,
     parseApiDatetime,
     zeropad,
-} from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
-import { OMDelete, EmbeddedOMFieldComponent } from "@/videoag/object_management/OMConfigComponent";
+    filesizeToHuman,
+    timestampToString,
+    datetimeToString,
+} from "@/videoag/miscellaneous";
+import { useEditMode, OMDelete, EmbeddedOMFieldComponent } from "@/videoag/object_management";
 
 export function getMediumQualityName(medium: publish_medium): string | undefined {
     switch (medium.medium_metadata.type) {
diff --git a/src/videoag/course/Player.tsx b/src/videoag/course/Player.tsx
index 55d52149559eeed5f8f88fa4cdc2bcdf54ed22f5..ac6027716a441a282fec70258d85e5b9b4dffe2d 100644
--- a/src/videoag/course/Player.tsx
+++ b/src/videoag/course/Player.tsx
@@ -11,19 +11,17 @@ import "@silvermine/videojs-quality-selector/dist/css/quality-selector.css";
 import VideoJsMarkers from "@/videoag/miscellaneous/videojs-markers";
 
 import type { course, chapter, publish_medium, lecture } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { ViewPermissionAuthorization } from "@/videoag/authentication/ViewPermissions";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import Title from "@/videoag/miscellaneous/TitleComponent";
-import { UpdateOverlay } from "@/videoag/miscellaneous/UpdateOverlay";
-import { showInfoToast, parseApiDatetime } from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useApi } from "@/videoag/api";
+import { ViewPermissionAuthorization } from "@/videoag/authentication";
+import { useLanguage } from "@/videoag/localization";
+import { Title, UpdateOverlay, showInfoToast, parseApiDatetime } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMDelete,
     OMEdit,
     OMHistory,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
+} from "@/videoag/object_management";
 import { basePath } from "@/../basepath";
 
 import { LectureCard, urlForLecture, getLectureThumbnailUrlNoPlaceholder } from "./Lecture";
@@ -33,7 +31,7 @@ import {
     getNamedPublishMedia,
 } from "./Medium";
 import { Chapters, AddChapterButton } from "./Chapter";
-import { StatTracker } from "./Stats";
+import { StatTracker } from "./StatTracker";
 
 import { PlayerData } from "@/pages/dynamic_player";
 
diff --git a/src/videoag/course/Stats.tsx b/src/videoag/course/StatTracker.tsx
similarity index 99%
rename from src/videoag/course/Stats.tsx
rename to src/videoag/course/StatTracker.tsx
index 4710f3e6b1929f2001079c3b500a49eb33f427fc..073c7284d9e50dfabd7f1eb555481bbb43231c25 100644
--- a/src/videoag/course/Stats.tsx
+++ b/src/videoag/course/StatTracker.tsx
@@ -2,9 +2,8 @@ import VideoJsPlayerType from "video.js/dist/types/player";
 import { DateTime } from "luxon";
 
 import type { int, datetime } from "@/videoag/api/types";
-import { Backend } from "@/videoag/api/Backend";
-import { ApiError } from "@/videoag/api/ApiError";
-import { formatApiDatetime } from "@/videoag/miscellaneous/Util";
+import { Backend, ApiError } from "@/videoag/api";
+import { formatApiDatetime } from "@/videoag/miscellaneous";
 
 const MAX_SEGMENT_REALTIME_DURATION_SEC = 20;
 const MIN_SEGMENT_DURATION_SEC = 1.2;
diff --git a/src/videoag/course/index.ts b/src/videoag/course/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ccd0e9c8d5f829b2b586cee977c6b5017a2b1604
--- /dev/null
+++ b/src/videoag/course/index.ts
@@ -0,0 +1,12 @@
+export { default as CourseListing } from "./CourseListing";
+export { LectureListItem, LectureCard } from "./Lecture";
+export { LiveLabel, LectureLiveLabel } from "./LiveLabel";
+export {
+    sortPublishMediaByQuality,
+    getSortedDownloadablePublishMedia,
+    getMediumFileNameForDownload,
+    getSortedPlayerPublishMedia,
+    PublishMediumList,
+    PublishMediumDownloadButton,
+} from "./Medium";
+export { EmbeddedPlayer, default as Player } from "./Player";
diff --git a/src/videoag/error/ErrorDisplay.tsx b/src/videoag/error/ErrorDisplay.tsx
index 48107ea4d0cfe126c29e20e6654ef939fe8994ef..5abf82afd7ac5833daa71e6b35965d3f8944740a 100644
--- a/src/videoag/error/ErrorDisplay.tsx
+++ b/src/videoag/error/ErrorDisplay.tsx
@@ -3,8 +3,8 @@ import { toast, Bounce } from "react-toastify";
 import { useRouter } from "next/router";
 import Link from "next/link";
 
-import { ApiError } from "@/videoag/api/ApiError";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { ApiError } from "@/videoag/api";
+import { useReloadBoundary } from "@/videoag/miscellaneous";
 
 export function showError(error: Error, prefix_message = "Ein Fehler ist aufgetreten") {
     if (error instanceof ApiError) {
diff --git a/src/videoag/error/FallbackErrorBoundary.tsx b/src/videoag/error/FallbackErrorBoundary.tsx
index c355099adf8802bab63346f15bd8d0cec8d5b40c..207a59b66b49e691b0bb1b67a28f6c7bcb8f61a7 100644
--- a/src/videoag/error/FallbackErrorBoundary.tsx
+++ b/src/videoag/error/FallbackErrorBoundary.tsx
@@ -1,7 +1,7 @@
 import React from "react";
 import { ErrorInfo } from "react";
 
-import { NestedReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { NestedReloadBoundary } from "@/videoag/miscellaneous";
 
 export class FallbackErrorBoundary extends React.Component<any, { error?: any }> {
     constructor(props: { fallback: (e: any) => React.ReactNode; children: React.ReactNode }) {
diff --git a/src/videoag/error/index.ts b/src/videoag/error/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..25fee30be355027203b6965e2d87d1fdd3c747e0
--- /dev/null
+++ b/src/videoag/error/index.ts
@@ -0,0 +1,8 @@
+export {
+    showError,
+    showErrorToast,
+    showWarningToast,
+    Four04,
+    ErrorComponent,
+} from "./ErrorDisplay";
+export { FallbackErrorBoundary } from "./FallbackErrorBoundary";
diff --git a/src/videoag/form/TypeEditor.tsx b/src/videoag/form/TypeEditor.tsx
index 0f9b9aadce6b89c453c69fd211d267b376003d2b..a9a049852c391b130f40b98ce9818c9ca826487b 100644
--- a/src/videoag/form/TypeEditor.tsx
+++ b/src/videoag/form/TypeEditor.tsx
@@ -2,8 +2,8 @@ import React, { useEffect, useRef, useState } from "react";
 import { DateTime } from "luxon";
 
 import type { int } from "@/videoag/api/types";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { deepEquals, parseApiDatetime, formatApiDatetime } from "@/videoag/miscellaneous/Util";
+import { useLanguage } from "@/videoag/localization";
+import { deepEquals, parseApiDatetime, formatApiDatetime } from "@/videoag/miscellaneous";
 
 export type EditorArgs = {
     value?: any;
diff --git a/src/videoag/form/index.ts b/src/videoag/form/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fe62678ef091b572a6bab2fae38f6c6fd29a2e0b
--- /dev/null
+++ b/src/videoag/form/index.ts
@@ -0,0 +1,9 @@
+export {
+    type EditorArgs,
+    StringEditor,
+    BooleanEditor,
+    IntEditor,
+    DatetimeEditor,
+    ChooserEditor,
+    JsonEditor,
+} from "./TypeEditor";
diff --git a/src/videoag/job/Job.tsx b/src/videoag/job/Job.tsx
index 781239c3f208120e9411329a71a24890a267cc05..480d4c1a949af168473984d1851124c66b6884b4 100644
--- a/src/videoag/job/Job.tsx
+++ b/src/videoag/job/Job.tsx
@@ -1,11 +1,13 @@
 import { useState, useEffect, useRef } from "react";
 
 import type { int, job, job_state } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { datetimeToString } from "@/videoag/miscellaneous/Formatting";
-import { Spinner } from "@/videoag/miscellaneous/Util";
-import { useDebouncedCall } from "@/videoag/miscellaneous/PromiseHelpers";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
+import { useApi } from "@/videoag/api";
+import {
+    datetimeToString,
+    Spinner,
+    useDebouncedCall,
+    useReloadBoundary,
+} from "@/videoag/miscellaneous";
 
 export function JobStatusCard({ jobId }: { jobId: int }) {
     const api = useApi();
diff --git a/src/videoag/job/index.ts b/src/videoag/job/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d570abc1c2c713a637f6ad13b0d7c7909d0c246e
--- /dev/null
+++ b/src/videoag/job/index.ts
@@ -0,0 +1 @@
+export { JobStatusCard } from "./Job";
diff --git a/src/videoag/localization/LanguageProvider.tsx b/src/videoag/localization/LanguageProvider.tsx
index 7d49b9328f9920348a0a23c591f609739febe12f..4624c1cb0d5bce6960e2dddc6d3ba692a3e0df8e 100644
--- a/src/videoag/localization/LanguageProvider.tsx
+++ b/src/videoag/localization/LanguageProvider.tsx
@@ -1,8 +1,8 @@
 import type React from "react";
 import { createContext, useContext, useState, useEffect } from "react";
 
-import { useApi } from "@/videoag/api/ApiProvider";
-import { storageGetOrDefault, storageSet } from "@/videoag/miscellaneous/Storage";
+import { useApi } from "@/videoag/api";
+import { storageGetOrDefault, storageSet } from "@/videoag/miscellaneous";
 
 import { Language } from "./i18n";
 
diff --git a/src/videoag/localization/i18n.tsx b/src/videoag/localization/i18n.tsx
index af63b4a1caea2f37373b7e390558400167cdc1de..06b764bc1e115bc69575040711ddc77ced0eab33 100644
--- a/src/videoag/localization/i18n.tsx
+++ b/src/videoag/localization/i18n.tsx
@@ -1,6 +1,6 @@
 import type React from "react";
 
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
+import { StylizedText } from "@/videoag/miscellaneous";
 
 export class Language {
     constructor(
diff --git a/src/videoag/localization/index.ts b/src/videoag/localization/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bc7b7211f2d838d0fba5605e964a3b129c70db29
--- /dev/null
+++ b/src/videoag/localization/index.ts
@@ -0,0 +1,2 @@
+export { Language } from "./i18n";
+export { LanguageProvider, useLanguage } from "./LanguageProvider";
diff --git a/src/videoag/miscellaneous/Theme.tsx b/src/videoag/miscellaneous/Theme.tsx
index cedb7be7f51d822357077c6b566196909b8d67ae..86b206182e713457b3d3bfe78f850d2cd119ccff 100644
--- a/src/videoag/miscellaneous/Theme.tsx
+++ b/src/videoag/miscellaneous/Theme.tsx
@@ -1,7 +1,7 @@
 import type React from "react";
 import { createContext, useContext, useEffect } from "react";
 
-import { useLocalStorageState } from "@/videoag/miscellaneous/Storage";
+import { useLocalStorageState } from "./Storage";
 
 type ThemeContextType = {
     theme: string;
diff --git a/src/videoag/miscellaneous/index.ts b/src/videoag/miscellaneous/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d903cd63f3f839abbf0b56af378ae458121301a7
--- /dev/null
+++ b/src/videoag/miscellaneous/index.ts
@@ -0,0 +1,35 @@
+export { type ICalEvent, parseICal } from "./Calendar";
+export { ExpandableString } from "./ExpandableString";
+export {
+    semesterToHuman,
+    timestampToString,
+    datetimeToStringOnlyDate,
+    datetimeToStringOnlyTime,
+    datetimeToString,
+    filesizeToHuman,
+} from "./Formatting";
+export {
+    useDebounce,
+    useDebounceWithArgument,
+    useMutexCall,
+    useMutexProcessing,
+    useDebouncedCall,
+    useDebouncedProcessing,
+} from "./PromiseHelpers";
+export { ReloadBoundary, NestedReloadBoundary, useReloadBoundary } from "./ReloadBoundary";
+export { default as StopNavigation } from "./StopNavigation";
+export { storageGetOrDefault, storageSet, useLocalStorageState } from "./Storage";
+export { StylizedText } from "./StylizedText";
+export { ThemeProvider, useTheme } from "./Theme";
+export { default as Title } from "./TitleComponent";
+export { UpdateOverlay } from "./UpdateOverlay";
+export {
+    zeropad,
+    formatApiDatetime,
+    parseApiDatetime,
+    deepEquals,
+    mapMap,
+    showInfoToast,
+    TooltipButton,
+    Spinner,
+} from "./Util";
diff --git a/src/videoag/object_management/FilteredData.tsx b/src/videoag/object_management/FilteredData.tsx
index 170dc469a513c63eb96d1c5881174f8587f461f1..290565b72681e2fc7c7af5d9e697e06c1c83fab0 100644
--- a/src/videoag/object_management/FilteredData.tsx
+++ b/src/videoag/object_management/FilteredData.tsx
@@ -4,9 +4,8 @@ import { useSearchParams } from "next/navigation";
 import { useRouter } from "next/router";
 
 import type { int } from "@/videoag/api/types";
-import { ChooserEditor } from "@/videoag/form/TypeEditor";
-import { useDebounceWithArgument } from "@/videoag/miscellaneous/PromiseHelpers";
-import { mapMap } from "@/videoag/miscellaneous/Util";
+import { ChooserEditor } from "@/videoag/form";
+import { useDebounceWithArgument, mapMap } from "@/videoag/miscellaneous";
 
 // TODO sometimes search params are overridden with defaults on page load (e.g. sorter files). Redo this and use a
 // boundary instead of passing filters through the parent
diff --git a/src/videoag/object_management/OMConfigComponent.tsx b/src/videoag/object_management/OMConfigComponent.tsx
index 02ff6b7b359829ecf09f45f422ff8df7fedb0254..178c4ec2b18625be025ad3e360553a84d993aa81 100644
--- a/src/videoag/object_management/OMConfigComponent.tsx
+++ b/src/videoag/object_management/OMConfigComponent.tsx
@@ -9,16 +9,19 @@ import type {
     field_value,
     int,
 } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { ApiError } from "@/videoag/api/ApiError";
-import { showError, showErrorToast } from "@/videoag/error/ErrorDisplay";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { useReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { useDebouncedProcessing } from "@/videoag/miscellaneous/PromiseHelpers";
-import { datetimeToString, semesterToHuman } from "@/videoag/miscellaneous/Formatting";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { deepEquals, TooltipButton } from "@/videoag/miscellaneous/Util";
-import StopNavigation from "@/videoag/miscellaneous/StopNavigation";
+import { useApi, ApiError } from "@/videoag/api";
+import { showError, showErrorToast } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import {
+    useReloadBoundary,
+    useDebouncedProcessing,
+    datetimeToString,
+    semesterToHuman,
+    StylizedText,
+    deepEquals,
+    TooltipButton,
+    StopNavigation,
+} from "@/videoag/miscellaneous";
 import { basePath } from "@/../basepath";
 
 import { LockEditMode, useEditMode } from "./EditModeProvider";
diff --git a/src/videoag/object_management/OMFieldEditor.tsx b/src/videoag/object_management/OMFieldEditor.tsx
index 0b45fececfd56fed99e41f29cd5635aa857884d8..f165bd964c47888c9348af8f7489f5eacb2f089c 100644
--- a/src/videoag/object_management/OMFieldEditor.tsx
+++ b/src/videoag/object_management/OMFieldEditor.tsx
@@ -9,7 +9,7 @@ import {
     DatetimeEditor,
     ChooserEditor,
     JsonEditor,
-} from "@/videoag/form/TypeEditor";
+} from "@/videoag/form";
 
 import { UserIdListEditor } from "./ObjectEditor";
 import ViewPermissionsEditor from "./ViewPermissionsEditor";
diff --git a/src/videoag/object_management/ObjectEditor.tsx b/src/videoag/object_management/ObjectEditor.tsx
index 2e169ce130daa701f8647504f6104f5c8a3cd8ed..43ed67b9cf90223966bbb4e513405a8303794fd2 100644
--- a/src/videoag/object_management/ObjectEditor.tsx
+++ b/src/videoag/object_management/ObjectEditor.tsx
@@ -2,9 +2,9 @@ import React, { useEffect, useRef, useState } from "react";
 import Dropdown from "react-bootstrap/Dropdown";
 
 import type { GetUsersResponse, user, int } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { EditorArgs } from "@/videoag/form/TypeEditor";
-import { showError } from "@/videoag/error/ErrorDisplay";
+import { useApi } from "@/videoag/api";
+import { EditorArgs } from "@/videoag/form";
+import { showError } from "@/videoag/error";
 
 export function UserIdListEditor({
     value,
diff --git a/src/videoag/object_management/ViewPermissionsEditor.tsx b/src/videoag/object_management/ViewPermissionsEditor.tsx
index c1da6eb09fac4d7581439da3a882456ba5bcb5e1..0085ca7d4807fd2f97740e08bd98bc4655eb07df 100644
--- a/src/videoag/object_management/ViewPermissionsEditor.tsx
+++ b/src/videoag/object_management/ViewPermissionsEditor.tsx
@@ -1,9 +1,9 @@
 import React, { useEffect, useRef, useState } from "react";
 
 import type { view_permissions, int } from "@/videoag/api/types";
-import { EditorArgs } from "@/videoag/form/TypeEditor";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { deepEquals } from "@/videoag/miscellaneous/Util";
+import { EditorArgs } from "@/videoag/form";
+import { useLanguage } from "@/videoag/localization";
+import { deepEquals } from "@/videoag/miscellaneous";
 
 function createListEditor<V>(
     disabled: boolean,
diff --git a/src/videoag/object_management/index.ts b/src/videoag/object_management/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..411b5bfa2b4dd1257691c9184dbe52653e949f80
--- /dev/null
+++ b/src/videoag/object_management/index.ts
@@ -0,0 +1,10 @@
+export { EditModeProvider, useEditMode } from "./EditModeProvider";
+export { useFilteredDataView, FilterInput, PagingNavigation } from "./FilteredData";
+export {
+    ListOMFieldComponent,
+    EmbeddedOMFieldComponent,
+    OMEdit,
+    OMCreate,
+    OMDelete,
+    OMHistory,
+} from "./OMConfigComponent";
diff --git a/src/videoag/site/AnnouncementComponent.tsx b/src/videoag/site/AnnouncementComponent.tsx
index 8453f9f5c4dcfc528c0b0b554ad80f500944349f..0ee5f4b61ccafddaeb14ffbc2d0a185775aafb9c 100644
--- a/src/videoag/site/AnnouncementComponent.tsx
+++ b/src/videoag/site/AnnouncementComponent.tsx
@@ -1,17 +1,15 @@
 import { useRouter } from "next/router";
 
 import type { announcement } from "@/videoag/api/types";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { useLocalStorageState } from "@/videoag/miscellaneous/Storage";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { TooltipButton } from "@/videoag/miscellaneous/Util";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
+import { useLanguage } from "@/videoag/localization/";
+import { useLocalStorageState, StylizedText, TooltipButton } from "@/videoag/miscellaneous";
 import {
+    useEditMode,
     OMCreate,
     OMDelete,
     OMEdit,
     EmbeddedOMFieldComponent,
-} from "@/videoag/object_management/OMConfigComponent";
+} from "@/videoag/object_management";
 
 // TODO mark non-visible (also check time) announcements for mods
 
diff --git a/src/videoag/site/DefaultLayout.tsx b/src/videoag/site/DefaultLayout.tsx
index 94e6a7947ed8a92327c2bbe433891bf82c19f3da..2f013406dab2aef463d46007b2d6c13caaa244a6 100644
--- a/src/videoag/site/DefaultLayout.tsx
+++ b/src/videoag/site/DefaultLayout.tsx
@@ -7,17 +7,13 @@ import Collapse from "react-bootstrap/Collapse";
 import Dropdown from "react-bootstrap/Dropdown";
 
 import type { GetStatusResponse } from "@/videoag/api/types";
-import { useApi } from "@/videoag/api/ApiProvider";
-import { useAuthStatus } from "@/videoag/authentication/AuthStatus";
-import UserField from "@/videoag/authentication/UserField";
-import { showErrorToast, ErrorComponent } from "@/videoag/error/ErrorDisplay";
-import { FallbackErrorBoundary } from "@/videoag/error/FallbackErrorBoundary";
-import { useLanguage } from "@/videoag/localization/LanguageProvider";
-import { ReloadBoundary } from "@/videoag/miscellaneous/ReloadBoundary";
-import { StylizedText } from "@/videoag/miscellaneous/StylizedText";
-import { useTheme } from "@/videoag/miscellaneous/Theme";
-import { useEditMode } from "@/videoag/object_management/EditModeProvider";
-import AnnouncementComponent from "@/videoag/site/AnnouncementComponent";
+import { useApi } from "@/videoag/api";
+import { useAuthStatus, UserField } from "@/videoag/authentication";
+import { showErrorToast, ErrorComponent, FallbackErrorBoundary } from "@/videoag/error";
+import { useLanguage } from "@/videoag/localization";
+import { ReloadBoundary, StylizedText, useTheme } from "@/videoag/miscellaneous";
+import { useEditMode } from "@/videoag/object_management";
+import { AnnouncementComponent } from "@/videoag/site";
 import { basePath } from "@/../basepath";
 
 function NavBarIcon({
diff --git a/src/videoag/site/index.ts b/src/videoag/site/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1c3ddd5ebd315b34c891d1658b8cf8a626ca7c7f
--- /dev/null
+++ b/src/videoag/site/index.ts
@@ -0,0 +1,2 @@
+export { default as AnnouncementComponent } from "./AnnouncementComponent";
+export { default as DefaultLayout, Search } from "./DefaultLayout";