npx skills add https://github.com/waynesutton/convexskills --skill convex-component-authoringHow Convex Component Authoring fits into a Paperclip company.
Convex Component Authoring drops into any Paperclip agent that handles convex and components work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
SKILL.md457 linesExpandCollapse
---name: convex-component-authoringdisplayName: Convex Component Authoringdescription: How to create, structure, and publish self-contained Convex components with proper isolation, exports, and dependency managementversion: 1.0.0author: Convextags: [convex, components, reusable, packages, npm]--- # Convex Component Authoring Create self-contained, reusable Convex components with proper isolation, exports, and dependency management for sharing across projects. ## Documentation Sources Before implementing, do not assume; fetch the latest documentation: - Primary: https://docs.convex.dev/components- Component Authoring: https://docs.convex.dev/components/authoring- For broader context: https://docs.convex.dev/llms.txt ## Instructions ### What Are Convex Components? Convex components are self-contained packages that include:- Database tables (isolated from the main app)- Functions (queries, mutations, actions)- TypeScript types and validators- Optional frontend hooks ### Component Structure ```my-convex-component/├── package.json├── tsconfig.json├── README.md├── src/│ ├── index.ts # Main exports│ ├── component.ts # Component definition│ ├── schema.ts # Component schema│ └── functions/│ ├── queries.ts│ ├── mutations.ts│ └── actions.ts└── convex.config.ts # Component configuration``` ### Creating a Component #### 1. Component Configuration ```typescript// convex.config.tsimport { defineComponent } from "convex/server"; export default defineComponent("myComponent");``` #### 2. Component Schema ```typescript// src/schema.tsimport { defineSchema, defineTable } from "convex/server";import { v } from "convex/values"; export default defineSchema({ // Tables are isolated to this component items: defineTable({ name: v.string(), data: v.any(), createdAt: v.number(), }).index("by_name", ["name"]), config: defineTable({ key: v.string(), value: v.any(), }).index("by_key", ["key"]),});``` #### 3. Component Definition ```typescript// src/component.tsimport { defineComponent, ComponentDefinition } from "convex/server";import schema from "./schema";import * as queries from "./functions/queries";import * as mutations from "./functions/mutations"; const component = defineComponent("myComponent", { schema, functions: { ...queries, ...mutations, },}); export default component;``` #### 4. Component Functions ```typescript// src/functions/queries.tsimport { query } from "../_generated/server";import { v } from "convex/values"; export const list = query({ args: { limit: v.optional(v.number()), }, returns: v.array(v.object({ _id: v.id("items"), name: v.string(), data: v.any(), createdAt: v.number(), })), handler: async (ctx, args) => { return await ctx.db .query("items") .order("desc") .take(args.limit ?? 10); },}); export const get = query({ args: { name: v.string() }, returns: v.union(v.object({ _id: v.id("items"), name: v.string(), data: v.any(), }), v.null()), handler: async (ctx, args) => { return await ctx.db .query("items") .withIndex("by_name", (q) => q.eq("name", args.name)) .unique(); },});``` ```typescript// src/functions/mutations.tsimport { mutation } from "../_generated/server";import { v } from "convex/values"; export const create = mutation({ args: { name: v.string(), data: v.any(), }, returns: v.id("items"), handler: async (ctx, args) => { return await ctx.db.insert("items", { name: args.name, data: args.data, createdAt: Date.now(), }); },}); export const update = mutation({ args: { id: v.id("items"), data: v.any(), }, returns: v.null(), handler: async (ctx, args) => { await ctx.db.patch(args.id, { data: args.data }); return null; },}); export const remove = mutation({ args: { id: v.id("items") }, returns: v.null(), handler: async (ctx, args) => { await ctx.db.delete(args.id); return null; },});``` #### 5. Main Exports ```typescript// src/index.tsexport { default as component } from "./component";export * from "./functions/queries";export * from "./functions/mutations"; // Export types for consumersexport type { Id } from "./_generated/dataModel";``` ### Using a Component ```typescript// In the consuming app's convex/convex.config.tsimport { defineApp } from "convex/server";import myComponent from "my-convex-component"; const app = defineApp(); app.use(myComponent, { name: "myComponent" }); export default app;``` ```typescript// In the consuming app's codeimport { useQuery, useMutation } from "convex/react";import { api } from "../convex/_generated/api"; function MyApp() { // Access component functions through the app's API const items = useQuery(api.myComponent.list, { limit: 10 }); const createItem = useMutation(api.myComponent.create); return ( <div> {items?.map((item) => ( <div key={item._id}>{item.name}</div> ))} <button onClick={() => createItem({ name: "New", data: {} })}> Add Item </button> </div> );}``` ### Component Configuration Options ```typescript// convex/convex.config.tsimport { defineApp } from "convex/server";import myComponent from "my-convex-component"; const app = defineApp(); // Basic usageapp.use(myComponent); // With custom nameapp.use(myComponent, { name: "customName" }); // Multiple instancesapp.use(myComponent, { name: "instance1" });app.use(myComponent, { name: "instance2" }); export default app;``` ### Providing Component Hooks ```typescript// src/hooks.tsimport { useQuery, useMutation } from "convex/react";import { FunctionReference } from "convex/server"; // Type-safe hooks for component consumersexport function useMyComponent(api: { list: FunctionReference<"query">; create: FunctionReference<"mutation">;}) { const items = useQuery(api.list, {}); const createItem = useMutation(api.create); return { items, createItem, isLoading: items === undefined, };}``` ### Publishing a Component #### package.json ```json{ "name": "my-convex-component", "version": "1.0.0", "description": "A reusable Convex component", "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ "dist", "convex.config.ts" ], "scripts": { "build": "tsc", "prepublishOnly": "npm run build" }, "peerDependencies": { "convex": "^1.0.0" }, "devDependencies": { "convex": "^1.17.0", "typescript": "^5.0.0" }, "keywords": [ "convex", "component" ]}``` #### tsconfig.json ```json{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "bundler", "declaration": true, "outDir": "dist", "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"]}``` ## Examples ### Rate Limiter Component ```typescript// rate-limiter/src/schema.tsimport { defineSchema, defineTable } from "convex/server";import { v } from "convex/values"; export default defineSchema({ requests: defineTable({ key: v.string(), timestamp: v.number(), }) .index("by_key", ["key"]) .index("by_key_and_time", ["key", "timestamp"]),});``` ```typescript// rate-limiter/src/functions/mutations.tsimport { mutation } from "../_generated/server";import { v } from "convex/values"; export const checkLimit = mutation({ args: { key: v.string(), limit: v.number(), windowMs: v.number(), }, returns: v.object({ allowed: v.boolean(), remaining: v.number(), resetAt: v.number(), }), handler: async (ctx, args) => { const now = Date.now(); const windowStart = now - args.windowMs; // Clean old entries const oldEntries = await ctx.db .query("requests") .withIndex("by_key_and_time", (q) => q.eq("key", args.key).lt("timestamp", windowStart) ) .collect(); for (const entry of oldEntries) { await ctx.db.delete(entry._id); } // Count current window const currentRequests = await ctx.db .query("requests") .withIndex("by_key", (q) => q.eq("key", args.key)) .collect(); const remaining = Math.max(0, args.limit - currentRequests.length); const allowed = remaining > 0; if (allowed) { await ctx.db.insert("requests", { key: args.key, timestamp: now, }); } const oldestRequest = currentRequests[0]; const resetAt = oldestRequest ? oldestRequest.timestamp + args.windowMs : now + args.windowMs; return { allowed, remaining: remaining - (allowed ? 1 : 0), resetAt }; },});``` ```typescript// Usage in consuming appimport { useMutation } from "convex/react";import { api } from "../convex/_generated/api"; function useRateLimitedAction() { const checkLimit = useMutation(api.rateLimiter.checkLimit); return async (action: () => Promise<void>) => { const result = await checkLimit({ key: "user-action", limit: 10, windowMs: 60000, }); if (!result.allowed) { throw new Error(`Rate limited. Try again at ${new Date(result.resetAt)}`); } await action(); };}``` ## Best Practices - Never run `npx convex deploy` unless explicitly instructed- Never run any git commands unless explicitly instructed- Keep component tables isolated (don't reference main app tables)- Export clear TypeScript types for consumers- Document all public functions and their arguments- Use semantic versioning for component releases- Include comprehensive README with examples- Test components in isolation before publishing ## Common Pitfalls 1. **Cross-referencing tables** - Component tables should be self-contained2. **Missing type exports** - Export all necessary types3. **Hardcoded configuration** - Use component options for customization4. **No versioning** - Follow semantic versioning5. **Poor documentation** - Document all public APIs ## References - Convex Documentation: https://docs.convex.dev/- Convex LLMs.txt: https://docs.convex.dev/llms.txt- Components: https://docs.convex.dev/components- Component Authoring: https://docs.convex.dev/components/authoringAvoid Feature Creep
Install Avoid Feature Creep skill for Claude Code from waynesutton/convexskills.
Convex
This is an umbrella skill that routes you to 11 specialized Convex development skills instead of trying to cram everything into one giant prompt. Hit `/convex-f
Convex Agents
Install Convex Agents skill for Claude Code from waynesutton/convexskills.