Skip to main content

Overview

Collections are user-defined groups that help you organize prompts by project, use case, team, or any other categorization that makes sense for your workflow. A prompt can belong to multiple collections.

Creating Collections

Basic Creation

Create a collection with:
  • Name (required): A descriptive name (1-50 characters)
  • Description (optional): Additional context (max 200 characters)
import { createCollection } from '@/features/collections/actions';

const result = await createCollection({
  name: 'Customer Support',
  description: 'Templates for handling customer inquiries',
});
Implementation from src/features/collections/actions.ts:8-39:
export async function createCollection(input: CreateCollectionInput) {
  const result = createCollectionSchema.safeParse(input);
  if (!result.success) {
    return { error: "Invalid input", details: result.error.format() };
  }

  const supabase = createClient(cookieStore);
  const { data: { user } } = await supabase.auth.getUser();

  if (!user) {
    return { error: "Unauthorized" };
  }

  const { data, error } = await supabase
    .from("collections")
    .insert({
      name: input.name,
      description: input.description,
      user_id: user.id
    })
    .select()
    .single();

  if (error) {
    return { error: error.message };
  }

  revalidatePath("/prompts");
  return { data };
}

Validation Schema

From src/features/collections/schemas.ts:3-6:
export const createCollectionSchema = z.object({
  name: z.string().min(1, "Name is required").max(50, "Name is too long"),
  description: z.string().max(200, "Description is too long").optional(),
});

Managing Collections

List Collections

Fetch all collections for the current user:
import { getCollections } from '@/features/collections/actions';

const { data } = await getCollections();
// Returns array sorted by name
Implementation from src/features/collections/actions.ts:41-56:
export async function getCollections() {
  const supabase = createClient(cookieStore);
  
  const { data, error } = await supabase
    .from("collections")
    .select("*")
    .order("name");
  
  if (error) {
    console.error("Error fetching collections:", error);
    return { data: [] };
  }

  return { data };
}

Update Collection

Modify a collection’s name or description:
import { updateCollection } from '@/features/collections/actions';

const result = await updateCollection(collectionId, {
  name: 'Customer Support Templates',
  description: 'Updated description',
});
Updates the updated_at timestamp automatically.

Delete Collection

Permanently delete a collection:
import { deleteCollection } from '@/features/collections/actions';

const result = await deleteCollection(collectionId);
Deleting a collection removes all associations with prompts but does not delete the prompts themselves. Prompts remain accessible.

Managing Prompt Associations

Add Prompt to Collection

Associate a prompt with a collection:
import { addToCollection } from '@/features/collections/actions';

const result = await addToCollection(promptId, collectionId);
Implementation from src/features/collections/actions.ts:101-118:
export async function addToCollection(
  promptId: string, 
  collectionId: string
) {
  const supabase = createClient(cookieStore);

  const { error } = await supabase
    .from("collection_prompts")
    .insert({
      prompt_id: promptId,
      collection_id: collectionId
    });

  if (error) {
    return { error: error.message };
  }

  revalidatePath("/prompts");
  return { error: null };
}

Remove Prompt from Collection

Disassociate a prompt from a collection:
import { removeFromCollection } from '@/features/collections/actions';

const result = await removeFromCollection(promptId, collectionId);
A prompt can belong to multiple collections. Removing it from one collection doesn’t affect its membership in others.

Database Schema

collections Table

From supabase/migrations/20260208000003_collections.sql:
CREATE TABLE collections (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  name TEXT NOT NULL,
  description TEXT,
  created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL,
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL
);

collection_prompts Join Table

Many-to-many relationship between collections and prompts:
CREATE TABLE collection_prompts (
  collection_id UUID NOT NULL REFERENCES collections(id) ON DELETE CASCADE,
  prompt_id UUID NOT NULL REFERENCES prompts(id) ON DELETE CASCADE,
  PRIMARY KEY (collection_id, prompt_id)
);
The composite primary key prevents duplicate associations.

Row Level Security

Users can only access their own collections:
CREATE POLICY "Users can view their own collections" 
  ON collections FOR SELECT 
  USING (auth.uid() = user_id);

CREATE POLICY "Users can view their own collection prompts" 
  ON collection_prompts FOR SELECT 
  USING (
    EXISTS (
      SELECT 1 FROM collections 
      WHERE id = collection_id AND user_id = auth.uid()
    )
  );

Search Integration

Filter by Collection

Search can be filtered to a specific collection:
import { searchPrompts } from '@/features/search/actions';

const { data } = await searchPrompts('customer', {
  collectionId: 'collection-uuid',
});
Implementation in the PostgreSQL search function:
AND (filter_collection_id IS NULL OR EXISTS (
  SELECT 1 FROM collection_prompts cp 
  WHERE cp.prompt_id = p.id AND cp.collection_id = filter_collection_id
))
From supabase/migrations/20260208000005_update_search_rpc.sql:56-59.

Use Cases

By Project

Collection: "Marketing Campaign Q1"
Prompts:
- Social media post generator
- Email subject lines
- Ad copy variations

By Team

Collection: "Engineering Team"
Prompts:
- Code review checklist
- Bug report template
- Technical documentation

By AI Model

Collection: "Claude-optimized"
Prompts:
- Prompts specifically tuned for Claude
- Examples with Claude's preferred style

By Stage

Collection: "Production Ready"
Prompts:
- Tested and approved prompts
- Used in live applications

Best Practices

Clear Naming

Use descriptive collection names that explain the grouping criteria. Avoid generic names like “Collection 1”.

Consistent Organization

Establish a consistent taxonomy. For example, always organize by project, or always by use case.

Multiple Collections

Don’t hesitate to add prompts to multiple collections. A customer support prompt might belong to both “Production” and “Customer Service”.

Archive Over Delete

Consider archiving prompts instead of removing them from collections. This preserves the organizational history.

Description for Context

Use collection descriptions to document the purpose, ownership, or any special considerations.

Limitations

No nested collections: Collections cannot contain other collections. Keep hierarchy flat.
No sharing: Collections are private to each user. To share prompts, use public sharing instead.
No collection-level permissions: All prompts in a collection must belong to the same user. No team collections (yet).

Advanced Features

Bulk Operations

While not implemented in the current API, you could build bulk operations using the existing actions:
// Add multiple prompts to a collection
async function bulkAddToCollection(
  promptIds: string[], 
  collectionId: string
) {
  const results = await Promise.all(
    promptIds.map(id => addToCollection(id, collectionId))
  );
  return results;
}

Collection Statistics

Query collection size using Supabase:
const { count } = await supabase
  .from('collection_prompts')
  .select('*', { count: 'exact', head: true })
  .eq('collection_id', collectionId);

Recently Updated Collections

Find collections with recent activity:
const { data } = await supabase
  .from('collections')
  .select('*')
  .order('updated_at', { ascending: false })
  .limit(10);

Next Steps

Search

Search within specific collections

Prompt Management

Learn about organizing prompts

Public Sharing

Share prompts outside of collections

MCP API

Access collections via API

Build docs developers (and LLMs) love