smithery.ai

Admin CRUD Page

Create admin dashboard pages with tables, forms, and actions

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 5,528 B
  • docs SUMMARY.md 83 B

History

  1. First recorded snapshot · 1 installs

SKILL.md

Admin CRUD Page Pattern

Use this skill when creating admin pages for managing entities.

When to Use

  • Adding a new admin section (e.g., /admin/[feature])
  • Creating admin list pages with tables and row actions
  • Creating admin edit/create forms with headers

Note: For general page patterns (forms, filters, pagination, remote functions), see [page-builder](../page-builder/SKILL.md).

Route Structure

Admin routes live in src/routes/(admin)/admin/:

src/routes/(admin)/admin/
└── [feature]/
    ├── +page.svelte          # List page (pure renderer)
    ├── data.remote.ts        # Remote functions (all logic here)
    ├── [id]/
    │   └── +page.svelte      # Edit page
    └── new/
        └── +page.svelte      # Create page (optional)

IMPORTANT: Never use +page.server.ts

This project uses Remote Functions exclusively for server-side logic. All data loading, form handling, and mutations must go through .remote.ts files. Never create +page.server.ts, +server.ts, or use SvelteKit form actions.

Admin-Specific Components

Component Purpose Import
PageHeader Page title with icon and actions $lib/ui/admin/PageHeader.svelte
Table Data table with snippets for header/row/actions $lib/ui/admin/Table.svelte
AdminList Simple wrapper with title and "New" button $lib/ui/admin/AdminList.svelte
StatusSelect Status filter dropdown $lib/ui/admin/StatusSelect.svelte
TypeSelect Content type filter $lib/ui/admin/TypeSelect.svelte
Badge Status/type badges $lib/ui/admin/Badge.svelte
Actions Row action buttons (edit, delete, custom) $lib/ui/admin/Actions
ContentPicker Select related content $lib/ui/admin/ContentPicker.svelte
QuickAction Dashboard quick action cards $lib/ui/admin/QuickAction.svelte
ConfirmWithDialog Confirmation dialog wrapper $lib/ui/admin/ConfirmWithDialog.svelte

Quick Start

List Page

<script lang="ts">
  import PageHeader from '$lib/ui/admin/PageHeader.svelte'
  import Table from '$lib/ui/admin/Table.svelte'
  import { Actions, Action } from '$lib/ui/admin/Actions'
  import FileText from 'phosphor-svelte/lib/FileText'
  import { getItems, deleteItem } from './data.remote'

  const items = await getItems()
</script>

<div class="container mx-auto space-y-8 px-2 py-6">
  <PageHeader
    title="Items"
    description="Manage all items"
    icon={FileText}
  />

  <Table action={true} data={items}>
    {#snippet header(classes)}
      <th class={classes}>Name</th>
      <th class={classes}>Status</th>
    {/snippet}
    {#snippet row(item, classes)}
      <td class={classes}>{item.name}</td>
      <td class={classes}>{item.status}</td>
    {/snippet}
    {#snippet actionCell(item)}
      <Actions id={item.id}>
        <Action.Edit href={`/admin/items/${item.id}`} />
        <Action.Delete form={deleteItem} />
      </Actions>
    {/snippet}
  </Table>
</div>

Edit Page

<script lang="ts">
  import { page } from '$app/state'
  import PageHeader from '$lib/ui/admin/PageHeader.svelte'
  import { initForm } from '$lib/utils/form.svelte'
  import { updateItem, getItem } from '../data.remote'
  import FileText from 'phosphor-svelte/lib/FileText'

  const itemId = page.params.id!
  const item = await getItem({ id: itemId })

  initForm(updateItem, () => ({
    id: itemId,
    name: item?.name ?? '',
    status: item?.status ?? 'draft'
  }))
</script>

<div class="container mx-auto space-y-8 px-2 py-6">
  <PageHeader
    title="Edit Item"
    description="Update item settings"
    icon={FileText}
  />

  <form {...updateItem} class="space-y-6">
    <input {...updateItem.fields.id.as('hidden', itemId)} />
    <!-- See page-builder/DETAIL-PAGE.md for form patterns -->
    <button type="submit">Save</button>
  </form>
</div>

Authorization

Always call checkAdminAuth() first in admin remote functions:

import { checkAdminAuth } from '../authorization.remote'

export const getItems = query('unchecked', async (searchParams) => {
	checkAdminAuth() // Throws if not admin
	// ... rest of logic
})

Reference Files

  • [LIST-PAGE.md](./LIST-PAGE.md) - Table, Actions, Badge patterns
  • [EDIT-PAGE.md](./EDIT-PAGE.md) - PageHeader, ConfirmWithDialog patterns
  • [TEMPLATES.md](./TEMPLATES.md) - Copy-paste starter templates

General Patterns

For patterns that apply to both admin and public pages, see:

  • [page-builder/LIST-PAGE.md](../page-builder/LIST-PAGE.md) - Filters, search, pagination
  • [page-builder/DETAIL-PAGE.md](../page-builder/DETAIL-PAGE.md) - Forms, validation
  • [page-builder/FEED-PAGE.md](../page-builder/FEED-PAGE.md) - Mixed-type feeds
  • [using-remote-functions/REMOTE-FIRST.md](../using-remote-functions/REMOTE-FIRST.md) - Remote-first architecture