diff --git a/src/components/ProtectedLayout.tsx b/src/components/ProtectedLayout.tsx
index 7e5f4c7849f54d175d6b6740d745cf2cd176fa1a..1f7aa9e60fe5bc6be2f2dc6c9699ac3b198c557b 100644
--- a/src/components/ProtectedLayout.tsx
+++ b/src/components/ProtectedLayout.tsx
@@ -1,5 +1,6 @@
 import NavWrapper from "@/components/NavWrapper";
 import { useUser } from "@/utils/context/AuthProvider";
+import GymApplicationProvider from "@/utils/context/GymApplicationProvider";
 import GymProvider from "@/utils/context/GymProvider";
 import { useEffect, useState } from "react";
 import { useNavigate, Outlet } from "react-router-dom";
@@ -29,7 +30,9 @@ function ProtectedLayout() {
   return (
     <NavWrapper>
       <GymProvider>
-        <Outlet />
+        <GymApplicationProvider>
+          <Outlet />
+        </GymApplicationProvider>
       </GymProvider>
     </NavWrapper>
   );
diff --git a/src/components/gym/GymApplication.tsx b/src/components/gym/GymApplication.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..6bc9080864ac36617c99462b20736de1eb7f135a
--- /dev/null
+++ b/src/components/gym/GymApplication.tsx
@@ -0,0 +1,145 @@
+import { useGymApplications } from "@/utils/context/GymApplicationProvider";
+import useField from "@/utils/hooks/useField";
+import { GymReturned } from "@/utils/validationSchema/gym";
+import validate from "@/utils/validationSchema/validate";
+import { useState } from "react";
+import { z } from "zod";
+import ErrorLabel from "../ErrorLabel";
+import { useMutation, useQueryClient } from "react-query";
+import {
+  GymApplicationReturned,
+  GymApplicationSent,
+} from "@/utils/validationSchema/gymApplication";
+import { useUser } from "@/utils/context/AuthProvider";
+import { applyToGym } from "@/utils/api/gymApplication";
+
+function GymApplication({ gym }: { gym: GymReturned }) {
+  const { gymApplications, gymApplicationsStatus } = useGymApplications();
+  const {
+    field: description,
+    fieldError: descriptionError,
+    setField: setDescription,
+    validate: validateDescription,
+  } = useField((description) => validate(z.string().min(1), description), "");
+  const [disabled, setDisabled] = useState(false);
+  const [applyError, setApplyError] = useState("");
+  const queryClient = useQueryClient();
+  const { status, user } = useUser();
+
+  const { mutateAsync: applyMutation } = useMutation({
+    mutationFn: async (payload: GymApplicationSent) => {
+      if (status !== "success" || user === undefined) {
+        return;
+      }
+      return await applyToGym(payload);
+    },
+    onMutate: async (addedApplication: GymApplicationSent) => {
+      await queryClient.cancelQueries({ queryKey: ["applications"] });
+
+      const previousGymApplications = gymApplications;
+
+      queryClient.setQueryData<GymApplicationReturned[] | undefined>(
+        ["applications"],
+        (oldGymApplications) => {
+          console.log("Previous gym applications", oldGymApplications);
+          if (!oldGymApplications) {
+            return oldGymApplications;
+          }
+          return [
+            ...oldGymApplications,
+            {
+              acceptance: 0,
+              description: addedApplication.application_description,
+              gymId: addedApplication.gym_id,
+              gymName: addedApplication.gym_name,
+            },
+          ];
+        }
+      );
+      return { previousGymApplications };
+    },
+    onError(_, __, context) {
+      queryClient.setQueryData(
+        ["applications"],
+        context?.previousGymApplications
+      );
+    },
+    onSuccess() {
+      console.log("Application", queryClient.getQueryData(["applications"]));
+
+      // queryClient.invalidateQueries(["applications"]);
+    },
+  });
+
+  async function handleSubmit() {
+    setDisabled(true);
+    if (descriptionError || user === undefined) {
+      return;
+    }
+
+    try {
+      await applyMutation({
+        application_description: description,
+        gym_id: gym.id,
+        gym_name: gym.name,
+        trainer_description: user.description,
+        trainer_name: user.name,
+        username: user.username,
+      });
+      setDescription("");
+    } catch (err: unknown) {
+      const error = err as { response: { data: { error: string } } };
+      setApplyError(error.response.data.error);
+    }
+    setDisabled(false);
+  }
+
+  if (gymApplicationsStatus !== "success") {
+    return <p>Loading...</p>;
+  }
+
+  if (!gymApplications) {
+    return <p>Something wrong happened</p>;
+  }
+
+  if (gymApplications.find(({ gymId }) => gymId === gym.id)) {
+    return (
+      <button className="btn btn-disabled" disabled>
+        Applied
+      </button>
+    );
+  }
+
+  return (
+    <>
+      <form
+        className="form-control gap-4 w-full"
+        onSubmit={(e) => {
+          e.preventDefault();
+          handleSubmit();
+        }}
+      >
+        <div className="flex flex-col">
+          <label className="label" htmlFor="description">
+            Description
+          </label>
+          <textarea
+            disabled={disabled}
+            id="description"
+            className="textarea textarea-bordered textarea-primary"
+            value={description}
+            onChange={(e) => {
+              setDescription(e.target.value);
+              validateDescription(e.target.value);
+            }}
+          />
+          <ErrorLabel error={descriptionError} />
+        </div>
+        <button className="btn btn-primary">Apply</button>
+      </form>
+      <ErrorLabel error={applyError} />
+    </>
+  );
+}
+
+export default GymApplication;
diff --git a/src/pages/gym/GymIndividual.tsx b/src/pages/gym/GymIndividual.tsx
index 6bb18accedc98d249cb00b14b7bfc4bb25a05d4c..79e5b192e1a6f761af1433540a4d8f2bfa3033db 100644
--- a/src/pages/gym/GymIndividual.tsx
+++ b/src/pages/gym/GymIndividual.tsx
@@ -1,15 +1,34 @@
+import GymApplication from "@/components/gym/GymApplication";
+import config from "@/utils/config";
 import { useGym } from "@/utils/context/GymProvider";
 import { useParams } from "react-router-dom";
 
 function GymIndividual() {
   const { id } = useParams();
-  const { data, status } = useGym(Number(id ?? 1));
-  if (!data || status !== "success") {
+  const { data: gym, status } = useGym(Number(id ?? 1));
+
+  if (status !== "success") {
     return "Loading...";
   }
+
+  if (!gym) {
+    return "Gym does not exist";
+  }
+
   return (
-    <div className="w-full flex flex-col max-w-7xl mx-auto">
-      <p>{data.name}</p>
+    <div className="w-full flex flex-col max-w-5xl mx-auto py-6 gap-4">
+      <img
+        src={`${config.GYM_MEDIA_URL}/${gym.pictureId}.jpg`}
+        className="w-full"
+      />
+      <div className="flex flex-col items-start justify-start text-left">
+        <h1>{gym.name}</h1>
+        <p>{gym.monthlyPrice} / month</p>
+        <p>{gym.averageRating} stars</p>
+        <p>{gym.cityName}</p>
+        <p>{gym.description}</p>
+      </div>
+      <GymApplication gym={gym} />
     </div>
   );
 }
diff --git a/src/utils/api/gymApplication.ts b/src/utils/api/gymApplication.ts
index ec6058973a93fce291a8bf9c8a8554fb192acd7b..d5afd3ec2825dcc3d76eb5ace244190ad86dbc4c 100644
--- a/src/utils/api/gymApplication.ts
+++ b/src/utils/api/gymApplication.ts
@@ -1,6 +1,7 @@
 import {
   GymApplicationFetched,
   GymApplicationReturned,
+  GymApplicationSent,
 } from "../validationSchema/gymApplication";
 
 export async function getGymApplication(): Promise<GymApplicationReturned[]> {
@@ -8,25 +9,46 @@ export async function getGymApplication(): Promise<GymApplicationReturned[]> {
   const myPromise = new Promise<GymApplicationFetched[]>(function (myResolve) {
     myResolve([
       {
-        acceptance: true,
+        acceptance: 0,
         application_description: "This is an application",
         gym_name: "BruhBruh",
+        gym_id: 1,
       },
       {
-        acceptance: true,
+        acceptance: 0,
         application_description: "This is an application as well",
         gym_name: "BruhBruhBruh",
+        gym_id: 2,
       },
     ]);
   });
 
   return (await myPromise).map(
-    ({ acceptance, application_description, gym_name }) => {
+    ({ acceptance, application_description, gym_name, gym_id }) => {
       return {
         acceptance,
         description: application_description,
         gymName: gym_name,
+        gymId: gym_id,
       };
     }
   );
 }
+
+export async function applyToGym({
+  application_description,
+  gym_id,
+  gym_name,
+}: GymApplicationSent): Promise<GymApplicationReturned> {
+  // return (await axios.get(`${config.NODE_JS_API}/api/gym/`, header)).data;
+  const myPromise = new Promise<GymApplicationReturned>(function (myResolve) {
+    myResolve({
+      acceptance: 0,
+      description: application_description,
+      gymId: gym_id,
+      gymName: gym_name,
+    });
+  });
+
+  return myPromise;
+}
diff --git a/src/utils/context/GymApplicationProvider.tsx b/src/utils/context/GymApplicationProvider.tsx
index 6cca1d30fc92758a25adde898f32f9cfdefcc2e2..a549a6b1c8d437ee0d5ea2afc50822a9357bd398 100644
--- a/src/utils/context/GymApplicationProvider.tsx
+++ b/src/utils/context/GymApplicationProvider.tsx
@@ -15,7 +15,7 @@ export function useGymApplications() {
   return useContext(GymApplicationsContext);
 }
 
-function GymProvider({ children }: { children: ReactNode }) {
+function GymApplicationProvider({ children }: { children: ReactNode }) {
   const { status: gymApplicationsStatus, data: gymApplications } = useQuery(
     ["applications"],
     getGymApplication,
@@ -37,4 +37,4 @@ function GymProvider({ children }: { children: ReactNode }) {
   );
 }
 
-export default GymProvider;
+export default GymApplicationProvider;
diff --git a/src/utils/context/GymProvider.tsx b/src/utils/context/GymProvider.tsx
index e23e0bc708a9ec91a85e1bf18c803ac5f4c02390..c7d85f501e4f288a119a41140f98592519ab85bd 100644
--- a/src/utils/context/GymProvider.tsx
+++ b/src/utils/context/GymProvider.tsx
@@ -23,7 +23,16 @@ export function useAllGyms() {
 export function useGym(id: number | null) {
   return useQuery(
     ["gyms", String(id)],
-    async () => (id ? await getGymById({ id }) : undefined),
+    async () => {
+      if (!id) {
+        return undefined;
+      }
+      try {
+        return await getGymById({ id });
+      } catch (error) {
+        return undefined;
+      }
+    },
     {
       cacheTime: 300000,
       retry: 3,
diff --git a/src/utils/validationSchema/gymApplication.ts b/src/utils/validationSchema/gymApplication.ts
index af74d6317f356c43c69c5827723b2363e87f863a..6214f6129ad2310105476415ba6107f26c2a4f5b 100644
--- a/src/utils/validationSchema/gymApplication.ts
+++ b/src/utils/validationSchema/gymApplication.ts
@@ -2,17 +2,30 @@ import { z } from "zod";
 
 const gymApplicationFetchedSchema = z.object({
   gym_name: z.string(),
-  acceptance: z.boolean(),
+  gym_id: z.number(),
+  acceptance: z.number().max(2),
   application_description: z.string(),
 });
 
 const gymApplicationReturnedSchema = z.object({
   gymName: z.string(),
-  acceptance: z.boolean(),
+  gymId: z.number(),
+  acceptance: z.number().max(2),
   description: z.string(),
 });
 
+const gymApplicationSentSchema = gymApplicationFetchedSchema
+  .extend({
+    trainer_name: z.string().max(50),
+    trainer_description: z.string().max(255),
+    username: z.string().max(50),
+  })
+  .omit({
+    acceptance: true,
+  });
+
 export type GymApplicationFetched = z.infer<typeof gymApplicationFetchedSchema>;
 export type GymApplicationReturned = z.infer<
   typeof gymApplicationReturnedSchema
 >;
+export type GymApplicationSent = z.infer<typeof gymApplicationSentSchema>;