diff --git a/Taskfile.yml b/Taskfile.yml
index 324e543069f580916221770d54a2175d42a1c716..1f7f8e6aa64f0274c76f91462fdbce35580bf7c4 100644
--- a/Taskfile.yml
+++ b/Taskfile.yml
@@ -380,7 +380,6 @@ tasks:
               vars:
                   BUILD_FLAGS: "{{.BUILD_FLAGS}}"
         cmds:
-            - templ generate
             - bun run build
 
     generate:bindings:
@@ -441,4 +440,5 @@ tasks:
     dev:reload:
         summary: Reloads the application
         cmds:
+            - templ generate
             - task: run
diff --git a/app.go b/app.go
deleted file mode 100644
index f4c485fdab5fc4ab1f35b661af301180289e9a81..0000000000000000000000000000000000000000
--- a/app.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package main
-
-import (
-	"github.com/a-h/templ"
-	"github.com/go-chi/chi/v5"
-	"github.com/go-chi/chi/v5/middleware"
-)
-
-func NewChiRouter() *chi.Mux {
-	r := chi.NewRouter()
-
-	r.Use(middleware.Logger)
-	r.Use(middleware.Recoverer)
-
-	r.Get("/test", templ.Handler(Hello()).ServeHTTP)
-
-	return r
-}
diff --git a/astro/services/Session.go b/astro/services/Session.go
index 02a718411b951dc0964585a8b43205f36246e302..8a19405a0ac4dfcd3243069a1dea58c27462de78 100644
--- a/astro/services/Session.go
+++ b/astro/services/Session.go
@@ -1,5 +1,7 @@
 package services
 
+import "fmt"
+
 const MinStageSize = 3
 
 // Session : Session details
@@ -8,7 +10,8 @@ type Session struct {
 	CompetitionNumber uint8
 }
 
-func (s *Session) AddCompetition(name string, category string, weapon string) {
+func (s *Session) AddCompetition(name string, category string, weapon string) bool {
+	fmt.Println("Adding competition")
 	competition := CreateCompetition(
 		s.CompetitionNumber,
 		name,
@@ -18,15 +21,19 @@ func (s *Session) AddCompetition(name string, category string, weapon string) {
 
 	s.Competitions = append(s.Competitions, &competition)
 	s.CompetitionNumber++
+
+	return true
 }
 
-func (s *Session) RemoveCompetition(competitionID uint8) {
+func (s *Session) RemoveCompetition(competitionID uint8) bool {
 	for i, competition := range s.Competitions {
 		if competition.CompetitionID == competitionID {
 			s.Competitions = append(s.Competitions[:i], s.Competitions[i+1:]...)
 		}
 	}
 	s.CompetitionNumber--
+
+	return true
 }
 
 func (s *Session) GetCompetitions() []Competition {
@@ -46,117 +53,9 @@ func (s *Session) GetCompetition(competitionID uint8) *Competition {
 	return nil
 }
 
-func (s *Session) AddPlayerToCompetition(competitionID uint8, player *Player) bool {
-	competition := s.GetCompetition(competitionID)
-	if competition == nil {
-		return false
-	}
-
-	if player == nil {
-		return false
-	}
-
-	// If player id is UINT16MAX, set it
-	var id uint16 = 0
-	for {
-		// Check if id is already taken
-		_, ok := competition.CompetitionPlayers[id]
-		if !ok {
-			break
-		}
-		id++
-	}
-
-	player.PlayerID = id
-
-	return competition.AddPlayer(player)
-}
-
-func (s *Session) RemovePlayerFromCompetition(competitionID uint8, player *Player) bool {
-	competition := s.GetCompetition(competitionID)
-	if competition == nil {
-		return false
+func (s *Session) GetLastCompetition() *Competition {
+	if len(s.Competitions) > 0 {
+		return s.Competitions[len(s.Competitions)-1]
 	}
-
-	return competition.RemovePlayer(player)
-}
-
-func (s *Session) GetAllPlayersFromCompetition(competitionID uint8) []*Player {
-	competition := s.GetCompetition(competitionID)
-	if competition == nil {
-		return []*Player{}
-	}
-
-	players := []*Player{}
-	for _, player := range competition.CompetitionPlayers {
-		players = append(players, player)
-	}
-	return players
-}
-
-func (s *Session) UpdateCompetitionPlayer(competitionID uint8, player *Player) bool {
-	competition := s.GetCompetition(competitionID)
-	if competition == nil {
-		return false
-	}
-
-	// TODO: Check if player is in competition
-
-	return competition.UpdatePlayer(player)
-}
-
-func (s *Session) GetStageKind(competitionID uint8, stageID uint8) StageKind {
-	competition := s.GetCompetition(competitionID)
-	if competition == nil {
-		return UNKNOWN
-	}
-
-	stage := competition.GetStage(stageID)
-	if stage == nil {
-		return UNKNOWN
-	}
-
-	return (*stage).GetKind()
-}
-
-func (s *Session) AddPlayerToCompetitionStage(competitionID uint8, stageID uint8, player *Player) bool {
-	competition := s.GetCompetition(competitionID)
-	if competition == nil {
-		return false
-	}
-
-	stage := competition.GetStage(stageID)
-	if stage == nil {
-		return false
-	}
-
-	return s.AddPlayerToCompetition(competitionID, player) && (*stage).AddPlayer(player)
-}
-
-func (s *Session) RemovePlayerFromCompetitionStage(competitionID uint8, stageID uint8, player *Player) bool {
-	competition := s.GetCompetition(competitionID)
-	if competition == nil {
-		return false
-	}
-
-	stage := competition.GetStage(stageID)
-	if stage == nil {
-		return false
-	}
-
-	return (*stage).RemovePlayer(player)
-}
-
-func (s *Session) GetPlayersFromCompetitionStage(competitionID uint8, stageID uint8) []*Player {
-	competition := s.GetCompetition(competitionID)
-	if competition == nil {
-		return []*Player{}
-	}
-
-	stage := competition.GetStage(stageID)
-	if stage == nil {
-		return []*Player{}
-	}
-
-	return (*stage).GetPlayers()
+	return nil
 }
diff --git a/astro/services/Util.go b/astro/services/Util.go
index 91d12d8408f9e12c529cc84689376e4b5ebf225f..51130b4af819449b846c924a15f855a8b13ee57d 100644
--- a/astro/services/Util.go
+++ b/astro/services/Util.go
@@ -1,5 +1,7 @@
 package services
 
+import "math/rand"
+
 // Category : Age category
 type Category uint8
 
@@ -127,3 +129,43 @@ func (s State) String() string {
 		return "Unknown"
 	}
 }
+
+// RGB : RGB color
+type RGB struct {
+	R uint8
+	G uint8
+	B uint8
+}
+
+func NewRGB(r, g, b uint8) RGB {
+	return RGB{R: r, G: g, B: b}
+}
+
+func (c RGB) String() string {
+	return string(c.R) + ", " + string(c.G) + ", " + string(c.B)
+}
+
+// export function randomColor(brightness: number): string {
+//   function randomChannel(brightness: number): string {
+//     var r = 255-brightness;
+//     var n = 0|((Math.random() * r) + brightness);
+//     var s = n.toString(16);
+//     return (s.length==1) ? '0'+s : s;
+//   }
+//   return '#' + randomChannel(brightness) + randomChannel(brightness) + randomChannel(brightness);
+// }
+
+func RandomChannel(brightness uint8) string {
+	r := 255 - brightness
+	n := uint8(rand.Uint32())%r + brightness
+	s := string(n)
+	if len(s) == 1 {
+		return "0" + s
+	}
+	return s
+}
+
+// RandomColor : Generate a random color
+func RandomColor(brightness uint8) string {
+	return "#" + RandomChannel(brightness) + RandomChannel(brightness) + RandomChannel(brightness)
+}
diff --git a/build/devmode.config.yaml b/build/devmode.config.yaml
index 1a441f2488200c8d4b9847c18fc1d6430eea4cf3..4d79e6439ca3ec50cd34e51de51b1deaa1ce67ac 100644
--- a/build/devmode.config.yaml
+++ b/build/devmode.config.yaml
@@ -1,28 +1,26 @@
 config:
-  root_path: .
-  log_level: warn
-  debounce: 1000
-  ignore:
-    dir:
-      - .git
-      - node_modules
-      - frontend
-      - bin
-    file:
-      - .DS_Store
-      - .gitignore
-      - .gitkeep
-    watched_extension:
-      - "*.go"
-    git_ignore: true
-  executes:
-    - cmd: wails3 task install:frontend:deps
-      type: once
-    - cmd: wails3 task dev:frontend
-      type: background
-    - cmd: go mod tidy
-      type: blocking
-    - cmd: wails3 task build
-      type: blocking
-    - cmd: wails3 task run
-      type: primary
+    root_path: .
+    log_level: warn
+    debounce: 1000
+    ignore:
+        dir:
+            - .git
+            - node_modules
+            - frontend
+            - bin
+        file:
+            - .DS_Store
+            - .gitignore
+            - .gitkeep
+        watched_extension:
+            - "*.go"
+        git_ignore: true
+    executes:
+        - cmd: wails3 task dev:frontend
+          type: background
+        - cmd: go mod tidy
+          type: blocking
+        - cmd: wails3 task build
+          type: blocking
+        - cmd: wails3 task run
+          type: primary
diff --git a/components/Hello.templ b/components/Hello.templ
new file mode 100644
index 0000000000000000000000000000000000000000..64140ae9374d000fbe218df5ae309b993a08c0a8
--- /dev/null
+++ b/components/Hello.templ
@@ -0,0 +1,7 @@
+package components
+
+templ Hello() {
+	<div>
+		Hello, World!
+	</div>
+}
diff --git a/components/Nav.templ b/components/Nav.templ
new file mode 100644
index 0000000000000000000000000000000000000000..67ec711cea9ebd6cf7410ec2b58c751828f01bb5
--- /dev/null
+++ b/components/Nav.templ
@@ -0,0 +1,35 @@
+package components
+
+import "astroproject/astro/services"
+
+templ CompetitionElement(competition *services.Competition) {
+	// Onclick, redirect to the competition page
+	<div class="competition text-blue-600 overline" hx-on:click={ Redirect(competition.CompetitionID) }>
+		{ competition.CompetitionName }
+	</div>
+}
+
+script Redirect(competitionID uint8) {
+	window.location.href = "/competition/" + competitionID
+}
+
+templ Nav(session *services.Session) {
+	<div
+		class="nav-bar"
+	>
+		<div class="competition-container">
+			for _, competition := range session.Competitions {
+				@CompetitionElement(competition)
+			}
+		</div>
+		<div class="competition-modifier">
+			<input
+				type="submit"
+				value="Add competition"
+				hx-post="/add-competition"
+				hx-target=".competition-container"
+				hx-swap="afterend"
+			/>
+		</div>
+	</div>
+}
diff --git a/components/Page.templ b/components/Page.templ
new file mode 100644
index 0000000000000000000000000000000000000000..ff634ee12d4c8281af90c76531442d5b0571ac85
--- /dev/null
+++ b/components/Page.templ
@@ -0,0 +1,24 @@
+package components
+
+import "astroproject/astro/services"
+
+templ Head() {
+	<head>
+		<meta charset="utf-8"/>
+		<meta name="viewport" content="width=device-width, initial-scale=1"/>
+		<title>AstroProject</title>
+		<link rel="stylesheet" href="/style.css"/>
+		<script src="https://unpkg.com/htmx.org"></script>
+	</head>
+}
+
+templ Page(contents templ.Component, session *services.Session) {
+	<!DOCTYPE html>
+	<html>
+		@Head()
+		<body>
+			@Nav(session)
+			@contents
+		</body>
+	</html>
+}
diff --git a/frontend/bindings/changeme/astro/services/club.ts b/frontend/bindings/changeme/astro/services/club.ts
deleted file mode 100644
index 52026561280e12a5eb5694750523cbe998b1e7cc..0000000000000000000000000000000000000000
--- a/frontend/bindings/changeme/astro/services/club.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import {Call as $Call, Create as $Create} from "@wailsio/runtime";
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import * as $models from "./models.js";
-
-export function Load(): Promise<$models.Club[]> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(705761780) as any;
-    let $typingPromise = $resultPromise.then(($result) => {
-        return $$createType1($result);
-    }) as any;
-    $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
-    return $typingPromise;
-}
-
-// Private type creation functions
-const $$createType0 = $models.Club.createFrom;
-const $$createType1 = $Create.Array($$createType0);
diff --git a/frontend/bindings/changeme/astro/services/competition.ts b/frontend/bindings/changeme/astro/services/competition.ts
deleted file mode 100644
index 54027ddc33b626cba7794ecc6c49193467f53f07..0000000000000000000000000000000000000000
--- a/frontend/bindings/changeme/astro/services/competition.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-
-/**
- * Competition : Competition details
- * @module
- */
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import {Call as $Call, Create as $Create} from "@wailsio/runtime";
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import * as $models from "./models.js";
-
-export function AddPlayer(player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(3220417277, player) as any;
-    return $resultPromise;
-}
-
-export function AddPlayerToStage(player: $models.Player, stage: $models.Stage): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2532203882, player, stage) as any;
-    return $resultPromise;
-}
-
-export function AddStage(stage: $models.Stage): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2261622806, stage) as any;
-    return $resultPromise;
-}
-
-export function FinishCompetition(): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(826570137) as any;
-    return $resultPromise;
-}
-
-export function GetStage(stageID: number): Promise<$models.Stage | null> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2570706223, stageID) as any;
-    return $resultPromise;
-}
-
-export function InitCompetition(): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(582268878) as any;
-    return $resultPromise;
-}
-
-export function RemovePlayer(player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(1491269932, player) as any;
-    return $resultPromise;
-}
-
-export function RemovePlayerFromStage(player: $models.Player, stage: $models.Stage): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(362483458, player, stage) as any;
-    return $resultPromise;
-}
-
-export function RemoveStage(stage: $models.Stage): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(1871882137, stage) as any;
-    return $resultPromise;
-}
-
-export function StartCompetition(): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(3322759170) as any;
-    return $resultPromise;
-}
-
-export function String(): Promise<string> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2560236652) as any;
-    return $resultPromise;
-}
-
-export function UpdatePlayer(player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(132875429, player) as any;
-    return $resultPromise;
-}
diff --git a/frontend/bindings/changeme/astro/services/index.ts b/frontend/bindings/changeme/astro/services/index.ts
deleted file mode 100644
index 042303c127d1b5e9c78b9d8675b94d89c97abddb..0000000000000000000000000000000000000000
--- a/frontend/bindings/changeme/astro/services/index.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-
-import * as Competition from "./competition.js";
-import * as Player from "./player.js";
-import * as Pool from "./pool.js";
-import * as Session from "./session.js";
-export {
-    Competition,
-    Player,
-    Pool,
-    Session
-};
-
-export * from "./models.js";
diff --git a/frontend/bindings/changeme/astro/services/models.ts b/frontend/bindings/changeme/astro/services/models.ts
deleted file mode 100644
index d5d7cf0c8cd5c4d8346997b3ddc30f34b953e25b..0000000000000000000000000000000000000000
--- a/frontend/bindings/changeme/astro/services/models.ts
+++ /dev/null
@@ -1,434 +0,0 @@
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import {Create as $Create} from "@wailsio/runtime";
-
-/**
- * Category : Age category
- */
-export enum Category {
-    /**
-     * The Go zero value for the underlying type of the enum.
-     */
-    $zero = 0,
-
-    /**
-     * 0
-     */
-    U7 = 0,
-
-    /**
-     * 1
-     */
-    U9 = 1,
-
-    /**
-     * 2
-     */
-    U11 = 2,
-
-    /**
-     * 3
-     */
-    U13 = 3,
-
-    /**
-     * 4
-     */
-    U15 = 4,
-
-    /**
-     * 5
-     */
-    U17 = 5,
-
-    /**
-     * 6
-     */
-    U20 = 6,
-
-    /**
-     * 7
-     */
-    SENIOR = 7,
-
-    /**
-     * 8
-     */
-    VETERAN = 8,
-};
-
-export class Club {
-    "club_id": number;
-    "club_name": string;
-
-    /** Creates a new Club instance. */
-    constructor($$source: Partial<Club> = {}) {
-        if (!("club_id" in $$source)) {
-            this["club_id"] = 0;
-        }
-        if (!("club_name" in $$source)) {
-            this["club_name"] = "";
-        }
-
-        Object.assign(this, $$source);
-    }
-
-    /**
-     * Creates a new Club instance from a string or object.
-     */
-    static createFrom($$source: any = {}): Club {
-        let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
-        return new Club($$parsedSource as Partial<Club>);
-    }
-}
-
-/**
- * Competition : Competition details
- */
-export class Competition {
-    /**
-     * 255 competitions max
-     */
-    "CompetitionID": number;
-    "CompetitionName": string;
-    "CompetitionCategory": Category;
-    "CompetitionWeapon": Weapon;
-    "CompetitionState": State;
-    "CompetitionMaxStageNumber": number;
-    "CompetitionStages": { [_: `${number}`]: Stage | null };
-    "CompetitionPlayers": { [_: `${number}`]: Player | null };
-    "CompetitionCurrentStageID": number;
-
-    /** Creates a new Competition instance. */
-    constructor($$source: Partial<Competition> = {}) {
-        if (!("CompetitionID" in $$source)) {
-            this["CompetitionID"] = 0;
-        }
-        if (!("CompetitionName" in $$source)) {
-            this["CompetitionName"] = "";
-        }
-        if (!("CompetitionCategory" in $$source)) {
-            this["CompetitionCategory"] = (0 as Category);
-        }
-        if (!("CompetitionWeapon" in $$source)) {
-            this["CompetitionWeapon"] = (0 as Weapon);
-        }
-        if (!("CompetitionState" in $$source)) {
-            this["CompetitionState"] = (0 as State);
-        }
-        if (!("CompetitionMaxStageNumber" in $$source)) {
-            this["CompetitionMaxStageNumber"] = 0;
-        }
-        if (!("CompetitionStages" in $$source)) {
-            this["CompetitionStages"] = {};
-        }
-        if (!("CompetitionPlayers" in $$source)) {
-            this["CompetitionPlayers"] = {};
-        }
-        if (!("CompetitionCurrentStageID" in $$source)) {
-            this["CompetitionCurrentStageID"] = 0;
-        }
-
-        Object.assign(this, $$source);
-    }
-
-    /**
-     * Creates a new Competition instance from a string or object.
-     */
-    static createFrom($$source: any = {}): Competition {
-        const $$createField6_0 = $$createType0;
-        const $$createField7_0 = $$createType3;
-        let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
-        if ("CompetitionStages" in $$parsedSource) {
-            $$parsedSource["CompetitionStages"] = $$createField6_0($$parsedSource["CompetitionStages"]);
-        }
-        if ("CompetitionPlayers" in $$parsedSource) {
-            $$parsedSource["CompetitionPlayers"] = $$createField7_0($$parsedSource["CompetitionPlayers"]);
-        }
-        return new Competition($$parsedSource as Partial<Competition>);
-    }
-}
-
-export class Nation {
-    "nation_id": number;
-    "nation_name": string;
-    "nation_code": string;
-
-    /** Creates a new Nation instance. */
-    constructor($$source: Partial<Nation> = {}) {
-        if (!("nation_id" in $$source)) {
-            this["nation_id"] = 0;
-        }
-        if (!("nation_name" in $$source)) {
-            this["nation_name"] = "";
-        }
-        if (!("nation_code" in $$source)) {
-            this["nation_code"] = "";
-        }
-
-        Object.assign(this, $$source);
-    }
-
-    /**
-     * Creates a new Nation instance from a string or object.
-     */
-    static createFrom($$source: any = {}): Nation {
-        let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
-        return new Nation($$parsedSource as Partial<Nation>);
-    }
-}
-
-/**
- * Player : Person details
- */
-export class Player {
-    /**
-     * More than 255 players
-     */
-    "PlayerID": number;
-    "PlayerFirstname": string;
-    "PlayerLastname": string;
-    "PlayerNation": Nation | null;
-    "PlayerRegion": Region | null;
-    "PlayerClub": Club | null;
-    "PlayerInitialRank": number;
-
-    /** Creates a new Player instance. */
-    constructor($$source: Partial<Player> = {}) {
-        if (!("PlayerID" in $$source)) {
-            this["PlayerID"] = 0;
-        }
-        if (!("PlayerFirstname" in $$source)) {
-            this["PlayerFirstname"] = "";
-        }
-        if (!("PlayerLastname" in $$source)) {
-            this["PlayerLastname"] = "";
-        }
-        if (!("PlayerNation" in $$source)) {
-            this["PlayerNation"] = null;
-        }
-        if (!("PlayerRegion" in $$source)) {
-            this["PlayerRegion"] = null;
-        }
-        if (!("PlayerClub" in $$source)) {
-            this["PlayerClub"] = null;
-        }
-        if (!("PlayerInitialRank" in $$source)) {
-            this["PlayerInitialRank"] = 0;
-        }
-
-        Object.assign(this, $$source);
-    }
-
-    /**
-     * Creates a new Player instance from a string or object.
-     */
-    static createFrom($$source: any = {}): Player {
-        const $$createField3_0 = $$createType5;
-        const $$createField4_0 = $$createType7;
-        const $$createField5_0 = $$createType9;
-        let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
-        if ("PlayerNation" in $$parsedSource) {
-            $$parsedSource["PlayerNation"] = $$createField3_0($$parsedSource["PlayerNation"]);
-        }
-        if ("PlayerRegion" in $$parsedSource) {
-            $$parsedSource["PlayerRegion"] = $$createField4_0($$parsedSource["PlayerRegion"]);
-        }
-        if ("PlayerClub" in $$parsedSource) {
-            $$parsedSource["PlayerClub"] = $$createField5_0($$parsedSource["PlayerClub"]);
-        }
-        return new Player($$parsedSource as Partial<Player>);
-    }
-}
-
-/**
- * Referee : Person details
- */
-export class Referee {
-    /**
-     * More than 255 referees
-     */
-    "RefereeID": number;
-    "RefereeFirstname": string;
-    "RefereeLastname": string;
-    "RefereeNationID": number;
-    "RefereeRegionID": number;
-    "RefereeClubID": number;
-
-    /** Creates a new Referee instance. */
-    constructor($$source: Partial<Referee> = {}) {
-        if (!("RefereeID" in $$source)) {
-            this["RefereeID"] = 0;
-        }
-        if (!("RefereeFirstname" in $$source)) {
-            this["RefereeFirstname"] = "";
-        }
-        if (!("RefereeLastname" in $$source)) {
-            this["RefereeLastname"] = "";
-        }
-        if (!("RefereeNationID" in $$source)) {
-            this["RefereeNationID"] = 0;
-        }
-        if (!("RefereeRegionID" in $$source)) {
-            this["RefereeRegionID"] = 0;
-        }
-        if (!("RefereeClubID" in $$source)) {
-            this["RefereeClubID"] = 0;
-        }
-
-        Object.assign(this, $$source);
-    }
-
-    /**
-     * Creates a new Referee instance from a string or object.
-     */
-    static createFrom($$source: any = {}): Referee {
-        let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
-        return new Referee($$parsedSource as Partial<Referee>);
-    }
-}
-
-export class Region {
-    "region_id": number;
-    "region_name": string;
-
-    /** Creates a new Region instance. */
-    constructor($$source: Partial<Region> = {}) {
-        if (!("region_id" in $$source)) {
-            this["region_id"] = 0;
-        }
-        if (!("region_name" in $$source)) {
-            this["region_name"] = "";
-        }
-
-        Object.assign(this, $$source);
-    }
-
-    /**
-     * Creates a new Region instance from a string or object.
-     */
-    static createFrom($$source: any = {}): Region {
-        let $$parsedSource = typeof $$source === 'string' ? JSON.parse($$source) : $$source;
-        return new Region($$parsedSource as Partial<Region>);
-    }
-}
-
-/**
- * Stage : Stage details
- */
-export type Stage = any;
-
-/**
- * Stage Kind
- */
-export enum StageKind {
-    /**
-     * The Go zero value for the underlying type of the enum.
-     */
-    $zero = 0,
-
-    /**
-     * 0
-     */
-    REGISTRATIONS = 0,
-
-    /**
-     * 1
-     */
-    POOLS = 1,
-
-    /**
-     * 2
-     */
-    POOLSRESULTS = 2,
-
-    /**
-     * 3
-     */
-    DIRECTELIMINATION = 3,
-
-    /**
-     * 4
-     */
-    FINALRANKING = 4,
-
-    /**
-     * 5
-     */
-    UNKNOWN = 5,
-};
-
-/**
- * State : Competition state
- */
-export enum State {
-    /**
-     * The Go zero value for the underlying type of the enum.
-     */
-    $zero = 0,
-
-    /**
-     * 0
-     */
-    REGISTERING = 0,
-
-    /**
-     * 1
-     */
-    STARTED = 1,
-
-    /**
-     * 2
-     */
-    FINISHED = 2,
-
-    /**
-     * 3
-     */
-    LOCKED = 3,
-
-    /**
-     * 4
-     */
-    IDLE = 4,
-};
-
-/**
- * Weapon : Weapon used in competition
- */
-export enum Weapon {
-    /**
-     * The Go zero value for the underlying type of the enum.
-     */
-    $zero = 0,
-
-    /**
-     * 0
-     */
-    FOIL = 0,
-
-    /**
-     * 1
-     */
-    EPEE = 1,
-
-    /**
-     * 2
-     */
-    SABRE = 2,
-};
-
-// Private type creation functions
-const $$createType0 = $Create.Map($Create.Any, $Create.Any);
-const $$createType1 = Player.createFrom;
-const $$createType2 = $Create.Nullable($$createType1);
-const $$createType3 = $Create.Map($Create.Any, $$createType2);
-const $$createType4 = Nation.createFrom;
-const $$createType5 = $Create.Nullable($$createType4);
-const $$createType6 = Region.createFrom;
-const $$createType7 = $Create.Nullable($$createType6);
-const $$createType8 = Club.createFrom;
-const $$createType9 = $Create.Nullable($$createType8);
diff --git a/frontend/bindings/changeme/astro/services/player.ts b/frontend/bindings/changeme/astro/services/player.ts
deleted file mode 100644
index f6a1447c21ad91ba5d246c852609badb27f59fcd..0000000000000000000000000000000000000000
--- a/frontend/bindings/changeme/astro/services/player.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-
-/**
- * Player : Person details
- * @module
- */
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import {Call as $Call, Create as $Create} from "@wailsio/runtime";
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import * as $models from "./models.js";
-
-export function GenerateRandomPlayer(): Promise<$models.Player | null> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2771769734) as any;
-    let $typingPromise = $resultPromise.then(($result) => {
-        return $$createType1($result);
-    }) as any;
-    $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
-    return $typingPromise;
-}
-
-export function String(): Promise<string> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2659461938) as any;
-    return $resultPromise;
-}
-
-// Private type creation functions
-const $$createType0 = $models.Player.createFrom;
-const $$createType1 = $Create.Nullable($$createType0);
diff --git a/frontend/bindings/changeme/astro/services/pool.ts b/frontend/bindings/changeme/astro/services/pool.ts
deleted file mode 100644
index 8d4f8dd50296c1bdcf840dcd0135edeaee8bbafc..0000000000000000000000000000000000000000
--- a/frontend/bindings/changeme/astro/services/pool.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import {Call as $Call, Create as $Create} from "@wailsio/runtime";
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import * as $models from "./models.js";
-
-export function AddPlayer(player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2419770436, player) as any;
-    return $resultPromise;
-}
-
-/**
- * ------------------------------ Pool ------------------------------
- */
-export function PlayerPosition(player: $models.Player | null): Promise<number> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(199697208, player) as any;
-    return $resultPromise;
-}
-
-export function RemovePlayer(player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2585363663, player) as any;
-    return $resultPromise;
-}
-
-export function SetReferee(referee: $models.Referee | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(1520561734, referee) as any;
-    return $resultPromise;
-}
-
-export function String(): Promise<string> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2027138107) as any;
-    return $resultPromise;
-}
diff --git a/frontend/bindings/changeme/astro/services/session.ts b/frontend/bindings/changeme/astro/services/session.ts
deleted file mode 100644
index de8fd51605c06a1c468627937ea52397e713d40f..0000000000000000000000000000000000000000
--- a/frontend/bindings/changeme/astro/services/session.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
-// This file is automatically generated. DO NOT EDIT
-
-/**
- * Session : Session details
- * @module
- */
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import {Call as $Call, Create as $Create} from "@wailsio/runtime";
-
-// eslint-disable-next-line @typescript-eslint/ban-ts-comment
-// @ts-ignore: Unused imports
-import * as $models from "./models.js";
-
-export function AddCompetition(name: string, category: string, weapon: string): Promise<void> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(3450672978, name, category, weapon) as any;
-    return $resultPromise;
-}
-
-export function AddPlayerToCompetition(competitionID: number, player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(392649030, competitionID, player) as any;
-    return $resultPromise;
-}
-
-export function AddPlayerToCompetitionStage(competitionID: number, stageID: number, player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2910612480, competitionID, stageID, player) as any;
-    return $resultPromise;
-}
-
-export function GetAllPlayersFromCompetition(competitionID: number): Promise<($models.Player | null)[]> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(3547487506, competitionID) as any;
-    let $typingPromise = $resultPromise.then(($result) => {
-        return $$createType2($result);
-    }) as any;
-    $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
-    return $typingPromise;
-}
-
-export function GetCompetition(competitionID: number): Promise<$models.Competition | null> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(3131533323, competitionID) as any;
-    let $typingPromise = $resultPromise.then(($result) => {
-        return $$createType4($result);
-    }) as any;
-    $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
-    return $typingPromise;
-}
-
-export function GetCompetitions(): Promise<$models.Competition[]> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(1300853992) as any;
-    let $typingPromise = $resultPromise.then(($result) => {
-        return $$createType5($result);
-    }) as any;
-    $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
-    return $typingPromise;
-}
-
-export function GetPlayersFromCompetitionStage(competitionID: number, stageID: number): Promise<($models.Player | null)[]> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(145932197, competitionID, stageID) as any;
-    let $typingPromise = $resultPromise.then(($result) => {
-        return $$createType2($result);
-    }) as any;
-    $typingPromise.cancel = $resultPromise.cancel.bind($resultPromise);
-    return $typingPromise;
-}
-
-export function GetStageKind(competitionID: number, stageID: number): Promise<$models.StageKind> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(3392771308, competitionID, stageID) as any;
-    return $resultPromise;
-}
-
-export function RemoveCompetition(competitionID: number): Promise<void> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(2428225511, competitionID) as any;
-    return $resultPromise;
-}
-
-export function RemovePlayerFromCompetition(competitionID: number, player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(1688030656, competitionID, player) as any;
-    return $resultPromise;
-}
-
-export function RemovePlayerFromCompetitionStage(competitionID: number, stageID: number, player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(4015938334, competitionID, stageID, player) as any;
-    return $resultPromise;
-}
-
-export function UpdateCompetitionPlayer(competitionID: number, player: $models.Player | null): Promise<boolean> & { cancel(): void } {
-    let $resultPromise = $Call.ByID(3540307881, competitionID, player) as any;
-    return $resultPromise;
-}
-
-// Private type creation functions
-const $$createType0 = $models.Player.createFrom;
-const $$createType1 = $Create.Nullable($$createType0);
-const $$createType2 = $Create.Array($$createType1);
-const $$createType3 = $models.Competition.createFrom;
-const $$createType4 = $Create.Nullable($$createType3);
-const $$createType5 = $Create.Array($$createType3);
diff --git a/frontend/bun.lockb b/frontend/bun.lockb
old mode 100755
new mode 100644
diff --git a/frontend/index.html b/frontend/index.html
index 503128f9de92570170d518c39f77c2c1b097ba5d..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1,18 +0,0 @@
-<html
-    class="font-[Inter,_Avenir,_Helvetica,_Arial,_sans-serif] text-[16px] leading-[24px] font-normal [color-scheme:light_dark] text-[rgba(255,_255,_255,_0.87)] bg-[#242424] antialiased"
-    lang="en">
-
-<head>
-    <meta charset="UTF-8" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <script src="https://unpkg.com/htmx.org"></script>
-    <link rel="stylesheet" href="/index.css" />
-    <title>Wails</title>
-</head>
-
-<body class="m-0 flex place-items-center min-w-[320px] min-h-screen">
-    <!-- On load, get /test and replace the content of the div with the response -->
-    <div id="me" hx-get="/test" hx-trigger="load" hx-target="#me" hx-swap="outerHTML"></div>
-</body>
-
-</html>
\ No newline at end of file
diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js
index 5992aa9cf621b1a39377aaae00f88867a3f19d9d..8c5c2dc99d18125ac8cf37e384422c825dbbd749 100644
--- a/frontend/tailwind.config.js
+++ b/frontend/tailwind.config.js
@@ -1,6 +1,6 @@
 /** @type {import('tailwindcss').Config} */
 module.exports = {
-    content: [ "./index.html"],
+    content: ["../components/*.templ"],
     theme: {
         extend: {},
     },
diff --git a/go.mod b/go.mod
index be29895064f6e59f1242437e3b9daa2632882d10..385c1c6f522d51bef8dfff241a091251898f3de9 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
 	github.com/a-h/templ v0.2.747
 	github.com/go-chi/chi/v5 v5.1.0
 	github.com/go-faker/faker/v4 v4.4.2
+	github.com/mavolin/go-htmx v1.0.0
 	github.com/wailsapp/wails/v3 v3.0.0-alpha.0
 )
 
diff --git a/go.sum b/go.sum
index e0d83362ed4d9d7d1ec582b83aee131dd5c383d5..f90e08da27f39b703da3dfe646e260a18989c5d2 100644
--- a/go.sum
+++ b/go.sum
@@ -78,6 +78,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mavolin/go-htmx v1.0.0 h1:43rZuemWd23zrMcTU939EsflXjOPxtHy9VraT1CZ6qQ=
+github.com/mavolin/go-htmx v1.0.0/go.mod h1:r6O09gzKou9kutq3UiDPZ//Q7IeBCMcs8US5/sHFbvg=
 github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
 github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
 github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
diff --git a/hello.templ b/hello.templ
deleted file mode 100644
index 5962d4e11def7d5a216b7bf66ee7a75694419ecd..0000000000000000000000000000000000000000
--- a/hello.templ
+++ /dev/null
@@ -1,5 +0,0 @@
-package main
-
-templ Hello() {
-	<div>Hello</div>
-}
diff --git a/main.go b/main.go
index e0a013353f81030fef90479ac1aa1ec4472da4c9..7631eff8bfd67a26076343ccebc3093fb9b24659 100644
--- a/main.go
+++ b/main.go
@@ -8,26 +8,20 @@ import (
 	"github.com/wailsapp/wails/v3/pkg/application"
 
 	"astroproject/astro/services"
+	"astroproject/routes"
 )
 
 var assets embed.FS
 
 func main() {
 
-	r := NewChiRouter()
-
 	session := services.Session{}
+	session.AddCompetition("Competition 1", "U7", "Foil")
+	r := routes.NewChiRouter(&session)
 
 	app := application.New(application.Options{
 		Name:        "AstroProject",
 		Description: "A demo of using raw HTML & CSS",
-		Services: []application.Service{
-			application.NewService(&session),
-			application.NewService(&services.Competition{}),
-			application.NewService(&services.Club{}),
-			application.NewService(&services.Pool{}),
-			application.NewService(&services.Player{}),
-		},
 		Assets: application.AssetOptions{
 			Handler: application.AssetFileServerFS(assets),
 			Middleware: func(next http.Handler) http.Handler {
@@ -43,9 +37,7 @@ func main() {
 	app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{
 		Title: "AstroProject",
 		Mac: application.MacWindow{
-			InvisibleTitleBarHeight: 50,
-			Backdrop:                application.MacBackdropTranslucent,
-			TitleBar:                application.MacTitleBarHiddenInset,
+			Backdrop: application.MacBackdropTranslucent,
 		},
 		BackgroundColour: application.NewRGB(27, 38, 54),
 		URL:              "/",
diff --git a/pages/Competition.templ b/pages/Competition.templ
new file mode 100644
index 0000000000000000000000000000000000000000..1989fbf7556245379bb46f6d3be044f576a09261
--- /dev/null
+++ b/pages/Competition.templ
@@ -0,0 +1,9 @@
+package pages
+
+import "astroproject/astro/services"
+
+templ CompetitionPage(competition *services.Competition) {
+	<h1>Competition Page</h1>
+	<h1>{ competition.CompetitionName }</h1>
+	<h2>{ competition.CompetitionCategory.String() }</h2>
+}
diff --git a/pages/Home.templ b/pages/Home.templ
new file mode 100644
index 0000000000000000000000000000000000000000..89e9dccd73d32016935bd038d517aa61e72e8ba1
--- /dev/null
+++ b/pages/Home.templ
@@ -0,0 +1,8 @@
+package pages
+
+templ HomePage() {
+	<h1>Welcome to AstroProject</h1>
+	<p>
+		AstroProject is a web application that allows you to create and manage your own competitions.
+	</p>
+}
diff --git a/routes/app.go b/routes/app.go
new file mode 100644
index 0000000000000000000000000000000000000000..7031ad0cc71ead9a30aa621e9e42b5d013b0a811
--- /dev/null
+++ b/routes/app.go
@@ -0,0 +1,44 @@
+package routes
+
+import (
+	"net/http"
+	"strconv"
+
+	"github.com/a-h/templ"
+	"github.com/go-chi/chi/v5"
+	"github.com/go-chi/chi/v5/middleware"
+
+	"astroproject/astro/services"
+	"astroproject/components"
+	"astroproject/pages"
+)
+
+// NewChiRouter creates a new chi router.
+func NewChiRouter(session *services.Session) *chi.Mux {
+	r := chi.NewRouter()
+
+	r.Use(middleware.Logger)
+	r.Use(middleware.Recoverer)
+
+	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
+		HXRender(w, r, pages.HomePage(), session)
+	})
+
+	r.Post("/add-competition", func(w http.ResponseWriter, r *http.Request) {
+		if session.AddCompetition("Competition", "U20", "Foil") {
+			templ.Handler(components.CompetitionElement(session.GetLastCompetition())).ServeHTTP(w, r)
+		}
+	})
+
+	r.Get("/competition/{competitionID}", func(w http.ResponseWriter, r *http.Request) {
+		competitionIDStr := chi.URLParam(r, "competitionID")
+		competitionID, err := strconv.ParseUint(competitionIDStr, 10, 8)
+		if err != nil {
+			// Return a 404.
+			http.NotFound(w, r)
+		}
+		HXRender(w, r, pages.CompetitionPage(session.GetCompetition(uint8(competitionID))), session)
+	})
+
+	return r
+}
diff --git a/routes/util.go b/routes/util.go
new file mode 100644
index 0000000000000000000000000000000000000000..73274f891520855c34133b8ef43ce6aa9b9755e5
--- /dev/null
+++ b/routes/util.go
@@ -0,0 +1,21 @@
+package routes
+
+import (
+	"net/http"
+
+	"github.com/a-h/templ"
+	"github.com/mavolin/go-htmx"
+
+	"astroproject/astro/services"
+	"astroproject/components"
+)
+
+func HXRender(w http.ResponseWriter, r *http.Request, component templ.Component, session *services.Session) {
+	hxRequest := htmx.Request(r)
+
+	if hxRequest == nil {
+		component = components.Page(component, session)
+	}
+	w.Header().Set("Content-Type", "text/html")
+	component.Render(r.Context(), w)
+}