Skip to main content
TOON components let you extract repeated patterns into reusable, parameterized blocks. A component is defined once and referenced by any step in the same file or imported from another .toon file.

Defining a Component

Use the top-level components: block:
name: review-pipeline

components:
  ReviewCycle:
    params:
      content: string
      reviewer: string
    steps[2]:
      - id: "{id}-review"
        prompt: "You are {params.reviewer}.\nReview the following content:\n{params.content}"
        output:
          approved: boolean
          feedback: string

      - id: "{id}-revise"
        prompt: "Revise based on feedback: {params.feedback}"
        output:
          content: string
        skipIf: "{id}-review.approved"

input:
  topic: string

steps[3]:
  - id: draft
    prompt: Write a draft about {input.topic}.
    output:
      content: string

  - id: tech-review
    kind: component
    use: ReviewCycle
    with:
      content: "{draft.content}"
      reviewer: "a senior engineer"

  - id: style-review
    kind: component
    use: ReviewCycle
    with:
      content: "{tech-review-revise.content}"
      reviewer: "a technical writer"

Component Parameters

Parameters are declared in the params: block and referenced with {params.fieldName} inside the component body:
components:
  Summarizer:
    params:
      text: string
      maxWords: number
      format: "bullet" | "prose"
    steps[1]:
      - id: "{id}-summarize"
        prompt: "Summarize the following text in at most {params.maxWords} words.\nFormat: {params.format}\nText: {params.text}"
        output:
          summary: string

ID Interpolation

Component step ids use {id} to prefix with the caller’s id, avoiding collisions when a component is used multiple times:
steps[2]:
  - id: first-pass
    kind: component
    use: Summarizer
    with:
      text: "{research.findings}"
      maxWords: 100
      format: "bullet"

  - id: second-pass
    kind: component
    use: Summarizer
    with:
      text: "{first-pass-summarize.summary}"
      maxWords: 50
      format: "prose"
The {id} token in the component definition is replaced with the caller’s id value at compile time.

Referencing Component Outputs

Access component outputs by their expanded step id:
steps[2]:
  - id: review
    kind: component
    use: ReviewCycle
    with:
      content: "{draft.content}"
      reviewer: "a tech lead"

  - id: publish
    prompt: "Publish the reviewed content:\n{review-revise.content}"
    output:
      url: string

Components with Control Flow

Components can contain any node kind — sequences, parallels, loops, approvals:
components:
  GuardedDeploy:
    params:
      version: string
      env: string
    steps[2]:
      - kind: approval
        id: "{id}-approve"
        request:
          title: "Deploy {params.version} to {params.env}?"
          summary: "Approval required for production deployment."
        onDeny: fail

      - id: "{id}-deploy"
        needs[1]: "{id}-approve"
        run: "await deployToEnv(params.env, params.version);"
        output:
          url: string

Importing Components

Components can be imported from other .toon files:
imports:
  components[2]{from,use}:
    ./shared/review.toon,ReviewCycle
    ./shared/deploy.toon,GuardedDeploy

name: full-pipeline
input:
  feature: string

steps[3]:
  - id: implement
    prompt: Implement {input.feature}.
    output:
      code: string

  - id: review
    kind: component
    use: ReviewCycle
    with:
      content: "{implement.code}"
      reviewer: "a senior engineer"

  - id: deploy
    kind: component
    use: GuardedDeploy
    with:
      version: "1.0.0"
      env: "production"

When to Use Components vs. Separate Workflows

Use components when:
  • The pattern is a reusable fragment within a larger workflow
  • You want to avoid duplicating step definitions
  • The fragment shares context (input, upstream outputs) with the parent workflow
Use separate workflows when:
  • The workflow is independently executable
  • It has its own input schema and lifecycle
  • It should be invocable from the CLI or API on its own

Next Steps

  • Prompts — Interpolation syntax inside prompts.
  • Imports — Import components, schemas, and services.
  • Nodes — All available node kinds.