diff --git a/src/components/DefaultLayout.tsx b/src/components/DefaultLayout.tsx
index f398f0117db4f47dd1a6b337c3bfd94f241df593..a86d92727292e3df20cdbf6d29886e94d3ddf73e 100644
--- a/src/components/DefaultLayout.tsx
+++ b/src/components/DefaultLayout.tsx
@@ -282,7 +282,13 @@ function UserField({ isUnavailable }: { isUnavailable: boolean }) {
     );
 }
 
-export function Search({ className }: { className?: string }) {
+export function Search({
+    className,
+    queryCallback,
+}: {
+    className?: string;
+    queryCallback?: (query: string) => void;
+}) {
     const router = useRouter();
     const [query, setQuery] = useState("");
     const { language } = useLanguage();
@@ -296,6 +302,7 @@ export function Search({ className }: { className?: string }) {
     };
     const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         setQuery(e.target.value);
+        if (queryCallback) queryCallback(e.target.value);
     };
 
     return (
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 045aa9fd11973658898a5801e5e65908d00729ed..15c71c2e7cadfcfa04b9dde42ff979b5e50f9228 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -11,7 +11,7 @@ import {
 } from "@/components/OMConfigComponent";
 import { ReloadBoundary, useReloadBoundary } from "@/components/ReloadBoundary";
 import { useUserContext } from "@/components/UserDataProvider";
-import { ResourceType, fetchDataWrapper } from "@/misc/PromiseHelpers";
+import { ResourceType, fetchDataWrapper, useDebounceWithArgument } from "@/misc/PromiseHelpers";
 import React from "react";
 import { ErrorPage, showError, showWarningToast } from "@/misc/ErrorHandlers";
 import { Suspense, useEffect, useState } from "react";
@@ -23,6 +23,7 @@ import { useRouter } from "next/router";
 import { VideoCard } from "@/components/VideoCard";
 import { LectureLiveLabel } from "@/components/LiveLabel";
 import { Search } from "@/components/DefaultLayout";
+import { SearchResults } from "./search";
 
 function FeatureCard({ data, all_featured }: { data: featured; all_featured: featured[] }) {
     const { editMode } = useEditMode();
@@ -257,6 +258,9 @@ export default function Home() {
     const userContext = useUserContext();
 
     const [homepageData, setHomepageData] = useState<ResourceType<GetHomepageResponse>>();
+    const [query, setQuery] = useState("");
+    const [updateQueryDeferred, _] = useDebounceWithArgument(setQuery, 500);
+
     const hasUserInfo = userContext.hasUserInfo();
     const { editMode } = useEditMode();
     const { language } = useLanguage();
@@ -289,8 +293,6 @@ export default function Home() {
 
     useEffect(reloadData, [api, hasUserInfo]);
 
-    if (homepageData === undefined) return <></>;
-
     return (
         <ReloadBoundary reloadFunc={reloadData}>
             <FallbackErrorBoundary
@@ -308,38 +310,46 @@ export default function Home() {
                             <h1 className="ms-3">VideoAG</h1>
                         </div>
 
-                        <Search className="mt-3" />
+                        <Search className="mt-3" queryCallback={updateQueryDeferred} />
                     </div>
                 </div>
                 <hr />
-                <div className="row">
-                    {editMode && <ModButtons />}
-                    <Suspense fallback={<div className="spinner-border" />}>
-                        <div className="col-md-6">
-                            <FeaturedContent homepageData={homepageData} />
-                        </div>
-                        <div className="col-md-6">
-                            <div className="card mb-3">
-                                <div className="card-body">
-                                    <h5 className="card-title">
-                                        {language.get("ui.index.next_recordings")}
-                                    </h5>
-
-                                    <UpcomingUploads homepageData={homepageData} />
-                                </div>
+                {query.length > 0 && (
+                    <>
+                        <SearchResults query={query} />
+                        <hr />
+                    </>
+                )}
+                {homepageData && (
+                    <div className="row">
+                        {editMode && <ModButtons />}
+                        <Suspense fallback={<div className="spinner-border" />}>
+                            <div className="col-md-6">
+                                <FeaturedContent homepageData={homepageData} />
                             </div>
-                            <div className="card mb-3">
-                                <div className="card-body">
-                                    <h5 className="card-title">
-                                        {language.get("ui.index.recent_videos")}
-                                    </h5>
-
-                                    <RecentUploads homepageData={homepageData} />
+                            <div className="col-md-6">
+                                <div className="card mb-3">
+                                    <div className="card-body">
+                                        <h5 className="card-title">
+                                            {language.get("ui.index.next_recordings")}
+                                        </h5>
+
+                                        <UpcomingUploads homepageData={homepageData} />
+                                    </div>
+                                </div>
+                                <div className="card mb-3">
+                                    <div className="card-body">
+                                        <h5 className="card-title">
+                                            {language.get("ui.index.recent_videos")}
+                                        </h5>
+
+                                        <RecentUploads homepageData={homepageData} />
+                                    </div>
                                 </div>
                             </div>
-                        </div>
-                    </Suspense>
-                </div>
+                        </Suspense>
+                    </div>
+                )}
             </FallbackErrorBoundary>
         </ReloadBoundary>
     );
diff --git a/src/pages/search.tsx b/src/pages/search.tsx
index 5b7b0e99429aaaced4a760547b7ee8fa39ce5511..034fc465c67c1567f7aff610c6327d9d4a448243 100644
--- a/src/pages/search.tsx
+++ b/src/pages/search.tsx
@@ -48,42 +48,23 @@ function LectureResults({
     );
 }
 
-export default function Search() {
+export function SearchResults({ query }: { query: string }) {
     const api = useBackendContext();
-    const params = useSearchParams();
-    const [query, setQuery] = useState(params.get("q"));
+    const { language } = useLanguage();
     const [searchResults, setSearchResults] = useState<SearchResponse>();
-    const workingQuery = useRef(query);
-    const [_, doForceReRender] = useState(0);
     const [error, setError] = useState<any>();
-    const router = useRouter();
-    const { language } = useLanguage();
-
-    // TODO: this code needs more cleanup, variable names are not very descriptive
-
-    useEffect(() => {
-        if (params.has("q")) setQuery(params.get("q"));
-        else if (query !== undefined && query !== null && query.length > 0) setQuery("");
-        // eslint-disable-next-line react-hooks/exhaustive-deps
-    }, [params]);
+    const fetchedQuery = useRef(query);
 
     const updateSearchResults = () => {
         setSearchResults(undefined);
-        if (query === undefined || query === null || query === "") {
-            if (params.has("q")) router.push({ search: new URLSearchParams().toString() });
+        if (query === "") {
             return;
         }
-        if (query !== params.get("q")) {
-            const newParams = new URLSearchParams(params.toString());
-            newParams.set("q", query);
 
-            router.push({ search: newParams.toString() });
-        }
-
-        workingQuery.current = query;
+        fetchedQuery.current = query;
         api.search(query)
             .then((results) => {
-                if (query !== workingQuery.current) return; // if the query has changed, don't update the results
+                if (query !== fetchedQuery.current) return; // if the query has changed, don't update the results
                 setSearchResults(results);
                 setError(undefined);
             })
@@ -91,35 +72,21 @@ export default function Search() {
                 setError(e);
             });
     };
-
     // eslint-disable-next-line react-hooks/exhaustive-deps
     useEffect(updateSearchResults, [query, api]);
 
-    const [updateQueryDeferred, updateQueryNow] = useDebounce(() => {
-        setQuery(workingQuery.current);
-    }, 500);
+    if (error) return <ErrorPage error={error} objectName="Suchergebnisse" objectPlural={true} />;
 
-    let results = (
-        <div className="spinner-border" role="status">
-            <span className="visually-hidden">Loading...</span>
-        </div>
-    );
-    if (error) {
-        results = <ErrorPage error={error} objectName="Suchergebnisse" objectPlural={true} />;
-    } else if (
-        query !== workingQuery.current &&
-        query !== undefined &&
-        query !== null &&
-        query !== ""
-    ) {
-        results = (
+    if (query !== fetchedQuery.current && query !== "")
+        return (
             <div className="spinner-border text-secondary" role="status">
                 <span className="visually-hidden">Loading...</span>
             </div>
         );
-    } else if (searchResults) {
-        results = (
-            <>
+
+    if (searchResults)
+        return (
+            <ReloadBoundary reloadFunc={updateSearchResults}>
                 {searchResults.courses.length > 0 && (
                     <CourseResults
                         courses={searchResults.courses.map(
@@ -136,11 +103,56 @@ export default function Search() {
                 {searchResults.courses.length === 0 && searchResults.lectures.length === 0 && (
                     <h4 className="text-muted">{language.get("ui.search.no_results")}</h4>
                 )}
-            </>
+            </ReloadBoundary>
         );
-    } else if (query === undefined || query === null || query === "") {
-        results = <h4 className="text-muted">{language.get("ui.search.no_query")}</h4>;
-    }
+
+    if (query === "") return <h4 className="text-muted">{language.get("ui.search.no_query")}</h4>;
+
+    return (
+        <div className="spinner-border" role="status">
+            <span className="visually-hidden">Loading...</span>
+        </div>
+    );
+}
+
+export default function Search() {
+    const params = useSearchParams();
+    const [query, setActualQuery] = useState(params.get("q") ?? "");
+    const router = useRouter();
+    const [_, doForceReRender] = useState(0);
+    const workingQuery = useRef(query);
+    const { language } = useLanguage();
+
+    // This code is somewhat complicated because we need to debounce the query update AND update url parameters
+    const [updateQueryDeferred, updateQueryNow] = useDebounce(() => {
+        const newQ = workingQuery.current;
+        setActualQuery(newQ);
+        if (newQ === undefined || newQ === null || newQ === "") {
+            if (params.has("q")) router.push({ search: new URLSearchParams().toString() });
+            return;
+        }
+        if (newQ !== params.get("q")) {
+            const newParams = new URLSearchParams(params.toString());
+            newParams.set("q", newQ);
+
+            router.push({ search: newParams.toString() });
+        }
+    }, 500);
+
+    useEffect(() => {
+        if (params.has("q")) {
+            if (query !== params.get("q") && workingQuery.current !== params.get("q")) {
+                workingQuery.current = params.get("q") ?? "";
+                updateQueryNow();
+                doForceReRender((r) => r + 1);
+            }
+        } else if (query.length > 0) {
+            workingQuery.current = "";
+            updateQueryNow();
+            doForceReRender((r) => r + 1);
+        }
+        // eslint-disable-next-line react-hooks/exhaustive-deps
+    }, [params]);
 
     const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
         workingQuery.current = e.target.value;
@@ -149,29 +161,27 @@ export default function Search() {
     };
 
     return (
-        <ReloadBoundary reloadFunc={updateSearchResults}>
-            <div>
-                <h1 className="d-flex align-items-center w-100">
-                    <span className="me-2">{language.get("ui.generic.search")}:</span>
-                    <form
-                        className="d-inline flex-fill"
-                        onSubmit={(e) => {
-                            e.preventDefault();
-                            updateQueryNow();
-                        }}
-                    >
-                        <input
-                            className="sneak-100-input fst-italic text-muted w-100 border-bottom"
-                            type="text"
-                            value={workingQuery.current || ""}
-                            onChange={onChange}
-                            autoFocus
-                            tabIndex={0}
-                        />
-                    </form>
-                </h1>
-                {results}
-            </div>
-        </ReloadBoundary>
+        <div>
+            <h1 className="d-flex align-items-center w-100">
+                <span className="me-2">{language.get("ui.generic.search")}:</span>
+                <form
+                    className="d-inline flex-fill"
+                    onSubmit={(e) => {
+                        e.preventDefault();
+                        updateQueryNow();
+                    }}
+                >
+                    <input
+                        className="sneak-100-input fst-italic text-muted w-100 border-bottom"
+                        type="text"
+                        value={workingQuery.current || ""}
+                        onChange={onChange}
+                        autoFocus
+                        tabIndex={0}
+                    />
+                </form>
+            </h1>
+            <SearchResults query={query} />
+        </div>
     );
 }