diff --git a/next-ui/src/components/announcement/Card.mdx b/next-ui/src/components/announcement/Card.mdx
new file mode 100644
index 000000000..b598c570e
--- /dev/null
+++ b/next-ui/src/components/announcement/Card.mdx
@@ -0,0 +1,11 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './Card.stories';
+
+
+
+# AnnouncementCard
+
+A card showing the details of a Komga announcement.
+
+
diff --git a/next-ui/src/components/release/Card.mdx b/next-ui/src/components/release/Card.mdx
new file mode 100644
index 000000000..ed4c40a14
--- /dev/null
+++ b/next-ui/src/components/release/Card.mdx
@@ -0,0 +1,11 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './Card.stories';
+
+
+
+# ReleaseCard
+
+A card showing the details of a Komga release.
+
+
diff --git a/next-ui/src/components/user/form/ChangePassword.mdx b/next-ui/src/components/user/form/ChangePassword.mdx
new file mode 100644
index 000000000..ffbea3db8
--- /dev/null
+++ b/next-ui/src/components/user/form/ChangePassword.mdx
@@ -0,0 +1,13 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './ChangePassword.stories';
+
+
+
+# UserChangePassword
+
+A form to update a user's password, which checks that both passwords are the same.
+
+Should be wrapped in a `` or `ConfirmEdit` dialog.
+
+
diff --git a/next-ui/src/fragments/fragment/BuildCommit.mdx b/next-ui/src/fragments/fragment/BuildCommit.mdx
new file mode 100644
index 000000000..2a83aacc8
--- /dev/null
+++ b/next-ui/src/fragments/fragment/BuildCommit.mdx
@@ -0,0 +1,11 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './BuildCommit.stories';
+
+
+
+# FragmentBuildCommit
+
+A button showing the current Github commit sha, retrieved from the Komga server. Clicking on the button will redirect to the particular commit at Github.
+
+
diff --git a/next-ui/src/fragments/fragment/BuildVersion.mdx b/next-ui/src/fragments/fragment/BuildVersion.mdx
new file mode 100644
index 000000000..aade792d2
--- /dev/null
+++ b/next-ui/src/fragments/fragment/BuildVersion.mdx
@@ -0,0 +1,11 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './BuildVersion.stories';
+
+
+
+# FragmentBuildVersion
+
+A button showing the current version of the Komga server. Will show a dot badge if a new update is available.
+
+
diff --git a/next-ui/src/fragments/fragment/LocaleSelector.mdx b/next-ui/src/fragments/fragment/LocaleSelector.mdx
new file mode 100644
index 000000000..156ec9563
--- /dev/null
+++ b/next-ui/src/fragments/fragment/LocaleSelector.mdx
@@ -0,0 +1,11 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './LocaleSelector.stories';
+
+
+
+# FragmentLocaleSelector
+
+A button that will display a menu when clicked, with a choice of available locales.
+
+
diff --git a/next-ui/src/fragments/fragment/LocaleSelector.stories.ts b/next-ui/src/fragments/fragment/LocaleSelector.stories.ts
new file mode 100644
index 000000000..ca8aab99b
--- /dev/null
+++ b/next-ui/src/fragments/fragment/LocaleSelector.stories.ts
@@ -0,0 +1,35 @@
+import type { Meta, StoryObj } from '@storybook/vue3-vite'
+
+import LocaleSelector from './LocaleSelector.vue'
+import { expect } from 'storybook/test'
+
+const meta = {
+ component: LocaleSelector,
+ render: (args: object) => ({
+ components: { LocaleSelector },
+ setup() {
+ return { args }
+ },
+ template: '',
+ }),
+ parameters: {
+ // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
+ },
+ args: {},
+} satisfies Meta
+
+export default meta
+type Story = StoryObj
+
+export const Default: Story = {
+ args: {},
+}
+
+export const Clicked: Story = {
+ args: {},
+ play: async ({ canvas, userEvent }) => {
+ await expect(canvas.getByRole('button')).toBeEnabled()
+
+ await userEvent.click(canvas.getByRole('button'))
+ },
+}
diff --git a/next-ui/src/fragments/fragment/SnackQueue.mdx b/next-ui/src/fragments/fragment/SnackQueue.mdx
new file mode 100644
index 000000000..19f06ed87
--- /dev/null
+++ b/next-ui/src/fragments/fragment/SnackQueue.mdx
@@ -0,0 +1,3 @@
+# FragmentSnackQueue
+
+A Vuetify [Snackbar Queue](https://vuetifyjs.com/en/components/snackbar-queue/#usage) that is hooked up to the Pinia `messagesStore.messages`.
diff --git a/next-ui/src/fragments/fragment/SnackQueue.stories.ts b/next-ui/src/fragments/fragment/SnackQueue.stories.ts
new file mode 100644
index 000000000..026e346ef
--- /dev/null
+++ b/next-ui/src/fragments/fragment/SnackQueue.stories.ts
@@ -0,0 +1,96 @@
+import type { Meta, StoryObj } from '@storybook/vue3-vite'
+
+import SnackQueue from './SnackQueue.vue'
+import { useMessagesStore } from '@/stores/messages'
+
+const meta = {
+ component: SnackQueue,
+ render: (args: object) => ({
+ components: { SnackQueue },
+ setup() {
+ return { args }
+ },
+ template: '',
+ }),
+ parameters: {
+ // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
+ },
+ args: {},
+} satisfies Meta
+
+export default meta
+type Story = StoryObj
+
+export const Default: Story = {
+ args: {},
+ play: () => {
+ const messagesStore = useMessagesStore()
+ messagesStore.messages = [{ text: 'Default notification' }]
+ },
+}
+
+export const NoTimeout: Story = {
+ args: {},
+ play: () => {
+ const messagesStore = useMessagesStore()
+ messagesStore.messages = [
+ {
+ text: 'I will not timeout, click me to make me go away',
+ timeout: -1,
+ timer: false,
+ },
+ ]
+ },
+}
+
+export const Success: Story = {
+ args: {},
+ play: () => {
+ const messagesStore = useMessagesStore()
+ messagesStore.messages = [
+ {
+ text: 'Success',
+ color: 'success',
+ },
+ ]
+ },
+}
+
+export const Warning: Story = {
+ args: {},
+ play: () => {
+ const messagesStore = useMessagesStore()
+ messagesStore.messages = [
+ {
+ text: 'Warning',
+ color: 'warning',
+ },
+ ]
+ },
+}
+
+export const Error: Story = {
+ args: {},
+ play: () => {
+ const messagesStore = useMessagesStore()
+ messagesStore.messages = [
+ {
+ text: 'Error',
+ color: 'error',
+ },
+ ]
+ },
+}
+
+export const Info: Story = {
+ args: {},
+ play: () => {
+ const messagesStore = useMessagesStore()
+ messagesStore.messages = [
+ {
+ text: 'Info',
+ color: 'info',
+ },
+ ]
+ },
+}
diff --git a/next-ui/src/fragments/fragment/SnackQueue.vue b/next-ui/src/fragments/fragment/SnackQueue.vue
index f5b3dc1b0..d6cda34e0 100644
--- a/next-ui/src/fragments/fragment/SnackQueue.vue
+++ b/next-ui/src/fragments/fragment/SnackQueue.vue
@@ -2,6 +2,7 @@
diff --git a/next-ui/src/fragments/fragment/ThemeSelector.mdx b/next-ui/src/fragments/fragment/ThemeSelector.mdx
new file mode 100644
index 000000000..d63c6efad
--- /dev/null
+++ b/next-ui/src/fragments/fragment/ThemeSelector.mdx
@@ -0,0 +1,11 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './ThemeSelector.stories';
+
+
+
+# FragmentThemeSelector
+
+A cycling button to change the theme.
+
+
diff --git a/next-ui/src/fragments/fragment/ThemeSelector.stories.ts b/next-ui/src/fragments/fragment/ThemeSelector.stories.ts
new file mode 100644
index 000000000..61ffe170d
--- /dev/null
+++ b/next-ui/src/fragments/fragment/ThemeSelector.stories.ts
@@ -0,0 +1,25 @@
+import type { Meta, StoryObj } from '@storybook/vue3-vite'
+
+import ThemeSelector from './ThemeSelector.vue'
+
+const meta = {
+ component: ThemeSelector,
+ render: (args: object) => ({
+ components: { ThemeSelector },
+ setup() {
+ return { args }
+ },
+ template: '',
+ }),
+ parameters: {
+ // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout
+ },
+ args: {},
+} satisfies Meta
+
+export default meta
+type Story = StoryObj
+
+export const Default: Story = {
+ args: {},
+}
diff --git a/next-ui/src/fragments/fragment/ThemeSelector.vue b/next-ui/src/fragments/fragment/ThemeSelector.vue
index 82dd81f9a..16aa35d38 100644
--- a/next-ui/src/fragments/fragment/ThemeSelector.vue
+++ b/next-ui/src/fragments/fragment/ThemeSelector.vue
@@ -2,6 +2,7 @@
diff --git a/next-ui/src/fragments/fragment/user/Table.mdx b/next-ui/src/fragments/fragment/user/Table.mdx
new file mode 100644
index 000000000..8c1258542
--- /dev/null
+++ b/next-ui/src/fragments/fragment/user/Table.mdx
@@ -0,0 +1,18 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './Table.stories';
+
+
+
+# FragmentUserTable
+
+A Vuetify [Data Table](https://vuetifyjs.com/en/components/data-tables/basics/) to display a list of users.
+
+Actions are available for each user:
+- Change password
+- Edit (not available for the current user)
+- Delete (not available for the current user)
+
+Actions are hooked up in the `users` page.
+
+
diff --git a/next-ui/src/fragments/fragment/user/form/CreateEdit.mdx b/next-ui/src/fragments/fragment/user/form/CreateEdit.mdx
new file mode 100644
index 000000000..484e88731
--- /dev/null
+++ b/next-ui/src/fragments/fragment/user/form/CreateEdit.mdx
@@ -0,0 +1,13 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './CreateEdit.stories';
+
+
+
+# FragmentUserFormCreateEdit
+
+A form to either create or edit a user.
+
+Should be wrapped in a `` or `ConfirmEdit` dialog.
+
+
diff --git a/next-ui/src/pages/server/announcements.mdx b/next-ui/src/pages/server/announcements.mdx
new file mode 100644
index 000000000..f8f0bcd1a
--- /dev/null
+++ b/next-ui/src/pages/server/announcements.mdx
@@ -0,0 +1,13 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './announcements.stories';
+
+
+
+# Announcements
+
+Page showing the announcements retrieved from the Komga server.
+
+Announcements can be marked as read either one by one, or in bulk using the floating action button.
+
+
diff --git a/next-ui/src/pages/server/updates.mdx b/next-ui/src/pages/server/updates.mdx
new file mode 100644
index 000000000..69b17e11f
--- /dev/null
+++ b/next-ui/src/pages/server/updates.mdx
@@ -0,0 +1,15 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './updates.stories';
+
+
+
+# Updates
+
+Page showing the updates (releases) retrieved from the Komga server.
+
+The latest and current release will display a chip next to the version number.
+
+The top banner will inform the user whether an update is available or not.
+
+
diff --git a/next-ui/src/pages/server/users.mdx b/next-ui/src/pages/server/users.mdx
new file mode 100644
index 000000000..35bf3b8b2
--- /dev/null
+++ b/next-ui/src/pages/server/users.mdx
@@ -0,0 +1,11 @@
+import { Canvas, Meta } from '@storybook/addon-docs/blocks';
+
+import * as Stories from './users.stories';
+
+
+
+# Users
+
+Page showing the users configured on the server.
+
+