ContentAIDOCS
Configuration

Customizing Templates

Add, edit, remove, or reorganize AI templates.

Every template in ContentAI lives in a single file: lib/templates.ts. No database, no admin UI — just plain TypeScript, which makes templates easy to edit and version-control.

Anatomy of a template

{
  id: "blog-post",                       // unique, kebab-case
  name: "Blog Post",                     // shown to the user
  description: "Generate a full blog post with title, intro, body, and conclusion",
  category: "blog",                      // one of the TemplateCategory values
  icon: "FileText",                      // Lucide icon name
  prompt: `Write a comprehensive blog post about "{topic}".
Tone: {tone}.
Target audience: {audience}.
Include a title, intro, body with subheadings, and conclusion.`,
  fields: [
    { name: "topic", label: "Topic", type: "text", placeholder: "e.g. Benefits of remote work", required: true },
    { name: "tone",  label: "Tone",  type: "select", placeholder: "Select tone",
      options: ["Professional", "Casual", "Friendly", "Authoritative"] },
    { name: "audience", label: "Target Audience", type: "text", placeholder: "e.g. SMB owners" },
  ],
}

Prompt placeholders

Anything inside curly braces like {topic} is replaced with the value of the field whose name matches. The Generate page handles interpolation automatically.

Field types

TypeRendered as
text<Input />
textarea<Textarea />
select<Select />

select requires an options array. All field types support required: true.

Add a new template

Append to the templates array in lib/templates.ts:

export const templates: Template[] = [
  // ... existing templates
  {
    id: "tweet-from-blog",
    name: "Tweet from Blog Post",
    description: "Turn a full blog post into a punchy Tweet",
    category: "social",
    icon: "Twitter",
    prompt: `Summarize this blog post into a single Tweet (max 280 chars).
Tone: {tone}.

Post:
{post}`,
    fields: [
      { name: "post", label: "Blog post", type: "textarea",
        placeholder: "Paste your blog post", required: true },
      { name: "tone", label: "Tone", type: "select", placeholder: "Select",
        options: ["Clever", "Professional", "Casual"] },
    ],
  },
];

The template is immediately available in Templates, Generate, and the Dashboard quick actions. No rebuild needed in dev — hot reload will pick it up.

Add a new category

Add an entry to categories in the same file:

export const categories = [
  // ...
  { id: "legal", label: "Legal", icon: "Gavel" },
];

Then add "legal" to the TemplateCategory union:

export type TemplateCategory =
  | "blog"
  | "marketing"
  | /* ... */
  | "legal";

Any template you assign to category: "legal" will show up under the new category.

Remove a template

Delete the entry from the templates array. If it was referenced from the Dashboard's "popular templates" list, remove that reference from app/(dashboard)/dashboard/page.tsx too.

Reorder templates

Templates display in the order they appear in the templates array. Reorder the array however you like.

Best practices for writing prompts

  1. Be specific about the output format. "Include a title, intro, body with subheadings, and conclusion" beats "Write a blog post".
  2. Always provide at least one tone field. Users expect control over voice.
  3. Keep fields minimal. 2–4 fields feels great; 7+ feels like a form from 2003.
  4. Use {placeholder} sparingly. Every {var} is one more thing the user has to fill.
  5. Test with short and long inputs. Models sometimes over- or under-produce based on input length.

Example: a high-quality SEO prompt

{
  id: "seo-cluster",
  name: "SEO Topic Cluster",
  description: "Generate a pillar topic + 8 supporting article ideas for internal linking",
  category: "seo",
  icon: "Network",
  prompt: `Act as an SEO strategist. Build a topic cluster around the pillar topic "{pillar}".

Return:
1. A pillar title (H1, max 60 chars).
2. A pillar description (140–160 chars).
3. 8 supporting article titles that link back to the pillar, each with:
   - Primary keyword
   - Search intent (informational/commercial/transactional)
   - A 1-sentence angle

Format as Markdown.`,
  fields: [
    { name: "pillar", label: "Pillar topic", type: "text",
      placeholder: "e.g. Remote work productivity", required: true },
  ],
}

Next: Security →

On this page