Skip to content
Back to skills

Prompt Tsx Patterns

ASecurity

Deep dive into prompt-tsx patterns used in vscode-copilot-chat, including component lifecycle, async rendering, priority system, and token budget management

  • 57 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 17, 2026
testingtypescriptgoreacttestingdocumentation

Works with

  • vscode

Security analysis

A100/100

Scanned October 17, 2026

npx -y skills add diegosouzapw/awesome-omni-skill --skill prompt-tsx-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Prompt Tsx Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Prompt Tsx Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/diegosouzapw-prompt-tsx-patterns/badge)](https://www.skillsdirectory.com/skills/diegosouzapw-prompt-tsx-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: prompt-tsx-patterns
description: Deep dive into prompt-tsx patterns used in vscode-copilot-chat, including component lifecycle, async rendering, priority system, and token budget management
keywords: prompt-tsx, vscode, prompts, react, tsx, token budget, priority
---

This skill provides comprehensive guidance on using prompt-tsx in the vscode-copilot-chat extension. It covers the specific patterns and conventions used in this codebase.

## What is Prompt-TSX?

Prompt-TSX is a React-like framework for building AI prompts using TypeScript and JSX. It provides:
- Component-based prompt composition
- Token budget management
- Priority-based pruning
- Type-safe prompt generation

## Core Concepts

### PromptElement Base Class

All prompt components extend `PromptElement`:

```typescript
import { PromptElement, BasePromptElementProps } from '@vscode/prompt-tsx';

interface MyPromptProps extends BasePromptElementProps {
  readonly userQuery: string;
  readonly files?: string[];
}

class MyPrompt extends PromptElement<MyPromptProps> {
  render() {
    return (
      <>
        <SystemMessage priority={1000}>
          System instructions here<br />
        </SystemMessage>
        <UserMessage priority={900}>
          {this.props.userQuery}
        </UserMessage>
      </>
    );
  }
}
```

**Key Points**:
- Props must extend `BasePromptElementProps`
- Render method returns JSX (PromptPiece)
- Can be sync or async
- Components are classes, not functions

### The Line Break Rule

**CRITICAL**: JSX collapses whitespace and newlines!

```typescript
// ❌ WRONG - These will be on the same line
<SystemMessage priority={1000}>
  You are a helpful assistant.
  Please follow these guidelines:
  1. Be concise
  2. Be accurate
</SystemMessage>

// ✅ CORRECT - Use <br /> for line breaks
<SystemMessage priority={1000}>
  You are a helpful assistant.<br />
  Please follow these guidelines:<br />
  1. Be concise<br />
  2. Be accurate<br />
</SystemMessage>
```

**Why**: Prompt-TSX renders to plain text. Without explicit `<br />` tags, all text is concatenated into a single line.

## Priority System

Priority controls:
1. **Rendering order** (high priority first)
2. **Pruning decisions** (low priority pruned first when over budget)

### Priority Ranges in This Codebase

Based on patterns in `src/extension/prompts/`:

```typescript
// Core instructions - always included
const PRIORITY_SYSTEM_INSTRUCTIONS = 1000;

// User's current message - highest user priority
const PRIORITY_USER_MESSAGE = 900;

// Recent conversation - important context
const PRIORITY_RECENT_HISTORY = 800;

// Conversation history - context but can be pruned
const PRIORITY_HISTORY = 700;

// User attachments - files, code snippets
const PRIORITY_ATTACHMENTS = 600;

// Contextual info - workspace, file listings
const PRIORITY_CONTEXT = 500;

// Documentation, examples - helpful but optional
const PRIORITY_BACKGROUND = 100;
```

### Priority Best Practices

1. **Space by 10s**: Use 700, 710, 720 not 700, 701, 702
2. **Group related content**: Similar priority for related pieces
3. **Consider pruning**: What should be removed first?
4. **Document choices**: Comment why you chose a priority

```typescript
// Good: Clear priority hierarchy
<>
  {/* Critical instructions - never prune */}
  <SystemMessage priority={1000}>...</SystemMessage>

  {/* User query - high priority */}
  <UserMessage priority={900}>...</UserMessage>

  {/* Recent context - medium priority */}
  <History priority={700} flexGrow={1} />

  {/* Background info - prune first */}
  <Documentation priority={100} />
</>
```

## Token Budget Management

### FlexGrow

`flexGrow` allows components to expand to fill available token space:

```typescript
// Component with flexGrow will use remaining tokens
<History
  priority={700}
  flexGrow={1}  // Take all remaining space
/>

// Multiple flex components share proportionally
<>
  <History priority={700} flexGrow={2} />    // Gets 2/3 of space
  <Examples priority={500} flexGrow={1} />   // Gets 1/3 of space
</>
```

### FlexReserve

`flexReserve` reserves tokens before rendering:

```typescript
<History
  priority={700}
  flexGrow={1}
  flexReserve="/5"  // Reserve 1/5 (20%) of budget before rendering
/>
```

**Use cases**:
- When you know minimum tokens needed
- Prevent other components from using all space
- Guarantee space for important but flex content

### TextChunk for Large Content

`TextChunk` enables intelligent truncation:

```typescript
<TextChunk
  breakOn="\n\n"      // Break on paragraph boundaries
  breakOnWhitespace   // Or break on any whitespace
  priority={500}
>
  {longDocumentation}
</TextChunk>
```

**How it works**:
- If content fits in budget: rendered fully
- If too large: truncated at break point
- Preserves readability by breaking cleanly

## Async Rendering

Components can perform async operations:

```typescript
class FileContentPrompt extends PromptElement<FileContentProps> {
  async render() {
    // Async work happens IN render
    const content = await this.readFile(this.props.filePath);
    const metadata = await this.getMetadata(this.props.filePath);

    return (
      <>
        <SystemMessage priority={1000}>
          File: {this.props.filePath}<br />
          Size: {metadata.size} bytes<br />
        </SystemMessage>
        <TextChunk priority={500} breakOnWhitespace>
          {content}
        </TextChunk>
      </>
    );
  }

  private async readFile(path: string): Promise<string> {
    // Implementation
  }
}
```

**Key points**:
- Use `async render()` for async operations
- All async work happens in render method
- Don't store promises in state
- Always await before returning JSX

## Special Components

### Tag

Create XML-like structured content:

```typescript
<Tag name="context" attrs={{ type: "file", id: "main.ts" }}>
  {fileContent}
</Tag>
```

Renders as:
```xml
<context type="file" id="main.ts">
  [fileContent]
</context>
```

**Use for**:
- Structured data in prompts
- Semantic markup
- Tool result formatting

### References

Track variable usage in prompts:

```typescript
<references value={[new PromptReference({ variableName: 'fileName' })]} />
```

**Purpose**: Tell the system which variables are used in the prompt

### Meta

Attach metadata that survives pruning:

```typescript
<meta value={new ToolResultMetadata(toolCallId, result)} />
```

**Purpose**: Preserve important metadata even if content is pruned

### KeepWith

Keep related content together during pruning:

```typescript
const KeepWith = useKeepWith();

<>
  <KeepWith priority={2}>
    <ToolCallRequest>...</ToolCallRequest>
  </KeepWith>
  <KeepWith priority={1}>
    <ToolCallResponse>...</ToolCallResponse>
  </KeepWith>
</>
```

**Effect**: Both elements pruned together, not separately

## Patterns from This Codebase

### Pattern: System + User Message

```typescript
render() {
  return (
    <>
      <SystemMessage priority={1000}>
        {this.props.systemInstructions}
      </SystemMessage>
      <UserMessage priority={900}>
        {this.props.userQuery}
      </UserMessage>
    </>
  );
}
```

### Pattern: History with Flex

```typescript
<History
  priority={700}
  flexGrow={1}
  flexReserve="/5"
  messages={this.props.conversationHistory}
/>
```

### Pattern: File Context

```typescript
<FileContext
  priority={600}
  flexGrow={2}
  files={this.props.attachedFiles}
/>
```

### Pattern: Tool Results

```typescript
{this.props.toolResults.map((result, i) => (
  <ToolResultComponent
    key={i}
    priority={850}  // Higher than history, lower than user message
    result={result}
  />
))}
```

## Common Mistakes

### 1. Forgetting Line Breaks

```typescript
// ❌ Will render on one line
<SystemMessage priority={1000}>
  Line 1
  Line 2
</SystemMessage>

// ✅ Explicit line breaks
<SystemMessage priority={1000}>
  Line 1<br />
  Line 2<br />
</SystemMessage>
```

### 2. Priority Conflicts

```typescript
// ❌ Same priority - unpredictable order
<SystemMessage priority={1000}>...</SystemMessage>
<AnotherMessage priority={1000}>...</AnotherMessage>

// ✅ Different priorities
<SystemMessage priority={1000}>...</SystemMessage>
<AnotherMessage priority={990}>...</AnotherMessage>
```

### 3. Async Without Await

```typescript
// ❌ Promise not awaited
async render() {
  const data = this.fetchData();  // Returns Promise!
  return <>{data}</>;  // Renders "[object Promise]"
}

// ✅ Await the promise
async render() {
  const data = await this.fetchData();
  return <>{data}</>;
}
```

### 4. Large Content Without TextChunk

```typescript
// ❌ Could exceed token budget
<UserMessage priority={900}>
  {hugeDocument}
</UserMessage>

// ✅ Use TextChunk for intelligent truncation
<TextChunk breakOnWhitespace priority={900}>
  {hugeDocument}
</TextChunk>
```

## Testing Prompt Components

### Manual Testing

1. **Create test props**:
   ```typescript
   const testProps: MyPromptProps = {
     userQuery: 'test query',
     files: ['file1.ts', 'file2.ts']
   };
   ```

2. **Instantiate and render**:
   ```typescript
   const prompt = new MyPrompt(testProps);
   const result = await prompt.render();
   ```

3. **Inspect output**:
   - Check priorities are correct
   - Verify line breaks appear
   - Confirm token usage reasonable

### Testing Strategies

- **Unit tests**: Test component logic
- **Integration tests**: Test full prompt composition
- **Token budget tests**: Test with tight budgets
- **Priority tests**: Verify pruning order

## Real Examples from This Codebase

See the `references/` directory for:
- `component-patterns.md` - Actual component implementations
- `priority-examples.md` - Priority usage patterns
- `async-rendering.md` - Async rendering examples

These reference files contain real code from this codebase that you can learn from and adapt.

## Quick Reference

**Must-know rules**:
1. ✅ Use `<br />` for line breaks
2. ✅ Props extend `BasePromptElementProps`
3. ✅ Higher priority = rendered first, pruned last
4. ✅ Use `async render()` for async operations
5. ✅ Use `TextChunk` for large content
6. ✅ Space priorities by 10s (700, 710, 720)

**Common components**:
- `<SystemMessage priority={N}>` - System instructions
- `<UserMessage priority={N}>` - User input
- `<TextChunk breakOn="...">` - Truncatable content
- `<Tag name="..." attrs={{}}>` - Structured markup
- `<references value={...} />` - Variable tracking
- `<meta value={...} />` - Metadata

Remember: Prompt-TSX is your interface to the AI. Master it, and you master how the AI sees and understands requests!

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.