{
    "app": {
        "name": "convex-component-authoring",
        "description": "How to create, structure, and publish self-contained Convex components with proper isolation, exports, and dependency management",
        "mode": "advanced-chat",
        "model_config": {
            "provider": "deepseek",
            "model": "deepseek-chat",
            "parameters": {
                "temperature": 0.7,
                "max_tokens": 4096
            }
        }
    },
    "instructions": "name convex-component-authoring displayName Convex Component Authoring description How to create, structure, and publish self-contained Convex components with proper isolation, exports, and dependency management version 1.0.0 author Convex tags [\"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 // convex.config.ts import { defineComponent } from \"convex/server\" ; export default defineComponent ( \"myComponent\" ); 2. Component Schema // src/schema.ts import { 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 // src/component.ts import { 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 // src/functions/queries.ts import { 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 (); }, }); // src/functions/mutations.ts import { 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 // src/index.ts export { default as component } from \"./component\" ; export * from \"./functions/queries\" ; export * from \"./functions/mutations\" ; // Export types for consumers export type { Id } from \"./_generated/dataModel\" ; Using a Component // In the consuming app's convex/convex.config.ts import { defineApp } from \"convex/server\" ; import myComponent from \"my-convex-component\" ; const app = defineApp (); app. use (myComponent, { name : \"myComponent\" }); export default app; // In the consuming app's code import { 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 // convex/convex.config.ts import { defineApp } from \"convex/server\" ; import myComponent from \"my-convex-component\" ; const app = defineApp (); // Basic usage app. use (myComponent); // With custom name app. use (myComponent, { name : \"customName\" }); // Multiple instances app. use (myComponent, { name : \"instance1\" }); app. use (myComponent, { name : \"instance2\" }); export default app; Providing Component Hooks // src/hooks.ts import { useQuery, useMutation } from \"convex/react\" ; import { FunctionReference } from \"convex/server\" ; // Type-safe hooks for component consumers export 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 { \"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 { \"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 // rate-limiter/src/schema.ts import { 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\" ]), }); // rate-limiter/src/functions/mutations.ts import { 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 }; }, }); // Usage in consuming app import { 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 (); }; }",
    "variables": [],
    "opening_statement": "你好，我是 convex-component-authoring，How to create, structure, and publish self-contain...",
    "suggested_questions": [],
    "source": "DeepseekModel",
    "source_url": "https://deepseekmodel.com/skill?id=waynesutton-convexskills-skills-convex-component-authoring-skill-md"
}