diff --git a/app/View/Page/Modal.php b/app/View/Page/Modal.php
new file mode 100644
index 00000000..072ff59f
--- /dev/null
+++ b/app/View/Page/Modal.php
@@ -0,0 +1,101 @@
+<?php
+
+namespace App\View\Page;
+
+use Livewire\Attributes\On;
+use Livewire\Component;
+
+class Modal extends Component
+{
+
+    public ?string $component = null;
+    public array $props = [];
+    public string $key = '';
+    public array $actions = [];
+    public ?string $size = null;
+    public string $title = '';
+
+    public function __construct()
+    {
+    }
+
+    #[On('openModal')]
+    public function onOpenModal(string $component, string $title, array $props = [], array $actions = ['storeable', 'closeable'], string $size = "xl"): void
+    {
+        $this->component = $component;
+        $this->props = $props;
+        $this->size = $size;
+        $this->title = $title;
+        $this->key = md5(json_encode(['component' => $component, 'props' => $props]));
+        $this->actions = $this->parseActions($actions);
+    }
+
+    public function parseActions(array $actions): array
+    {
+        return collect($actions)->map(function ($action) {
+            if ($action === 'closeable') {
+                return ['event' => 'closeModal', 'icon' => 'close', 'label' => 'Schließen'];
+            }
+
+            if ($action === 'storeable') {
+                return ['event' => 'onStoreFromModal', 'icon' => 'save', 'label' => 'Speichern'];
+            }
+
+            return $action;
+        })->toArray();
+    }
+
+    #[On('closeModal')]
+    public function onCloseModal(): void
+    {
+        $this->reset();
+    }
+
+    public function sizeClass(): string
+    {
+        if ($this->size === 'lg') {
+            return 'max-w-lg';
+        }
+
+        if ($this->size === 'xl') {
+            return 'max-w-xl';
+        }
+
+        return '';
+    }
+
+    public function render()
+    {
+        return <<<'HTML'
+            <div>
+                @if($component)
+                <div class="fixed z-40 top-0 left-0 w-full h-full flex items-center justify-center p-6 bg-black/60 backdrop-blur-sm" @click.self="$dispatch('closeModal')">
+                    <div
+                        class="relative rounded-lg p-8 bg-zinc-800 shadow-2xl shadow-black border border-zinc-700 border-solid w-full max-h-full flex flex-col overflow-auto {{$this->sizeClass()}}"
+                    >
+                        <div class="flex">
+                            <h3 class="font-semibold text-primary-200 text-xl grow">{{$title}}</h3>
+                            <div class="flex space-x-6">
+                                @foreach ($this->actions as $action)
+                                <a x-tooltip.raw="{{$action['label']}}" href="#" @click.prevent="$dispatch('{{$action['event']}}')">
+                                    <x-ui::sprite :src="$action['icon']" class="text-zinc-400 w-6 h-6"></x-ui::sprite>
+                                </a>
+                                @endforeach
+                            </div>
+                        </div>
+                        <div class="text-primary-100 group is-popup grow flex flex-col mt-3">
+                            <div>
+                                @if ($component)
+                                @livewire($component, $props, key($key))
+                                @endif
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                @else
+                <div></div>
+                @endif
+            </div>
+        HTML;
+    }
+}
diff --git a/resources/js/components/ui/Popup.vue b/resources/js/components/ui/Popup.vue
deleted file mode 100644
index 0c84fa42..00000000
--- a/resources/js/components/ui/Popup.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-<template>
-    <div class="fixed z-40 top-0 left-0 w-full h-full flex items-center justify-center p-6 bg-black/60 backdrop-blur-sm">
-        <div
-            class="relative rounded-lg p-8 bg-zinc-800 shadow-2xl shadow-black border border-zinc-700 border-solid w-full max-h-full flex flex-col overflow-auto"
-            :class="full ? 'h-full' : innerWidth"
-        >
-            <div class="absolute top-0 right-0 mt-6 mr-6 flex space-x-6">
-                <slot name="actions"></slot>
-                <a href="#" @click.prevent="$emit('close')">
-                    <ui-sprite src="close" class="text-zinc-400 w-6 h-6"></ui-sprite>
-                </a>
-            </div>
-            <h3 v-if="heading" class="font-semibold text-primary-200 text-xl" v-html="heading"></h3>
-            <div class="text-primary-100 group is-popup grow flex flex-col">
-                <suspense>
-                    <div>
-                        <slot></slot>
-                    </div>
-                    <template #fallback>
-                        <ui-loading></ui-loading>
-                    </template>
-                </suspense>
-            </div>
-        </div>
-    </div>
-</template>
-
-<script>
-export default {
-    props: {
-        heading: {
-            type: String,
-            default: () => '',
-        },
-        innerWidth: {
-            default: () => 'max-w-xl',
-            type: String,
-        },
-        full: {
-            type: Boolean,
-            default: () => false,
-        },
-    },
-};
-</script>