Semantic Primitives
AIDK provides a complete set of JSX components for building context. These primitives work with any renderer and are automatically formatted for each model.
Why Semantic Primitives?
Instead of string templates:
python
# ❌ String templates
prompt = f"""
## User Profile
Name: {user.name}
Tier: **{user.tier}**
1. Email: {user.email}
2. Member since: {user.createdAt}
"""Use typed, composable components:
tsx
// ✅ Semantic primitives
<Section audience="model">
<H2>User Profile</H2>
<Paragraph>
Name: {user.name} | Tier: <strong>{user.tier}</strong>
</Paragraph>
<List ordered>
<ListItem>Email: {user.email}</ListItem>
<ListItem>Member since: {user.createdAt}</ListItem>
</List>
</Section>Benefits:
- ✅ Type-safe
- ✅ IDE autocomplete
- ✅ Refactorable
- ✅ Testable
- ✅ Works with any renderer
- ✅ No escaping issues
Typography
Headings
tsx
<H1>Heading Level 1</H1>
<H2>Heading Level 2</H2>
<H3>Heading Level 3</H3>
<H4>Heading Level 4</H4>
<H5>Heading Level 5</H5>
<H6>Heading Level 6</H6>Markdown output:
markdown
# Heading Level 1
## Heading Level 2
### Heading Level 3XML output:
xml
<h1>Heading Level 1</h1>
<h2>Heading Level 2</h2>
<h3>Heading Level 3</h3>Paragraphs
tsx
<Paragraph>
This is a paragraph of text. It can contain inline formatting.
</Paragraph>
<Paragraph>
Multiple paragraphs are separated by blank lines.
</Paragraph>Inline Formatting
Bold / Strong
tsx
<Paragraph>
This text is <strong>bold and important</strong>.
</Paragraph>Italic / Emphasis
tsx
<Paragraph>
This text is <em>emphasized</em>.
</Paragraph>Inline Code
tsx
<Paragraph>
Use <inlineCode>console.log()</inlineCode> to debug.
</Paragraph>Highlight / Mark
tsx
<Paragraph>
This is <mark>highlighted text</mark>.
</Paragraph>Underline
tsx
<Paragraph>
This text is <u>underlined</u>.
</Paragraph>Strikethrough
tsx
<Paragraph>
This text is <s>crossed out</s> or <del>deleted</del>.
</Paragraph>Subscript & Superscript
tsx
<Paragraph>
H<sub>2</sub>O is water. E=mc<sup>2</sup> is Einstein's equation.
</Paragraph>Combined
tsx
<Paragraph>
Text with <strong>bold</strong>, <em>italic</em>,<inlineCode>code</inlineCode>
, <mark>highlight</mark>, and{" "}
<strong>
<em>both bold and italic</em>
</strong>
.
</Paragraph>Lists
Ordered Lists
tsx
<List ordered>
<ListItem>First item</ListItem>
<ListItem>Second item</ListItem>
<ListItem>Third item</ListItem>
</List>Markdown:
markdown
1. First item
2. Second item
3. Third itemXML:
xml
<ol>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ol>Unordered Lists
tsx
<List>
<ListItem>Bullet point</ListItem>
<ListItem>Another point</ListItem>
<ListItem>Third point</ListItem>
</List>Markdown:
markdown
- Bullet point
- Another point
- Third pointNested Lists
tsx
<List>
<ListItem>
Parent item
<List ordered>
<ListItem>Nested item 1</ListItem>
<ListItem>Nested item 2</ListItem>
</List>
</ListItem>
<ListItem>Another parent item</ListItem>
</List>Task Lists
Task lists render as checkboxes for tracking completion:
tsx
<List task>
<ListItem checked>Completed task</ListItem>
<ListItem checked={false}>Pending task</ListItem>
<ListItem>Also pending (defaults to unchecked)</ListItem>
</List>Markdown (GFM):
markdown
- [x] Completed task
- [ ] Pending task
- [ ] Also pending (defaults to unchecked)Markdown (CommonMark):
markdown
- ✓ Completed task
- ○ Pending task
- ○ Also pending (defaults to unchecked)XML:
xml
<ul class="task-list">
<li class="task-list-item"><input type="checkbox" checked disabled />Completed task</li>
<li class="task-list-item"><input type="checkbox" disabled />Pending task</li>
<li class="task-list-item"><input type="checkbox" disabled />Also pending (defaults to unchecked)</li>
</ul>Dynamic Task Lists
tsx
const tasks = [
{ id: 1, title: "Setup project", done: true },
{ id: 2, title: "Write tests", done: false },
{ id: 3, title: "Deploy", done: false },
];
<List task>
{tasks.map((task) => (
<ListItem key={task.id} checked={task.done}>
{task.title}
</ListItem>
))}
</List>;Tables
Simple Table
tsx
<Table
headers={["Name", "Age", "Role"]}
rows={[
["Alice", "30", "Engineer"],
["Bob", "25", "Designer"],
["Carol", "28", "Manager"],
]}
/>Markdown:
markdown
| Name | Age | Role |
| ----- | --- | -------- |
| Alice | 30 | Engineer |
| Bob | 25 | Designer |
| Carol | 28 | Manager |XML:
xml
<table>
<thead>
<tr><th>Name</th><th>Age</th><th>Role</th></tr>
</thead>
<tbody>
<tr><td>Alice</td><td>30</td><td>Engineer</td></tr>
<tr><td>Bob</td><td>25</td><td>Designer</td></tr>
<tr><td>Carol</td><td>28</td><td>Manager</td></tr>
</tbody>
</table>Dynamic Table
tsx
const users = [
{ name: "Alice", age: 30, role: "Engineer" },
{ name: "Bob", age: 25, role: "Designer" },
];
<Table
headers={["Name", "Age", "Role"]}
rows={users.map((u) => [u.name, String(u.age), u.role])}
/>;Table with Formatting
tsx
<Table
headers={["Metric", "Value", "Status"]}
rows={[
["Revenue", "$1.2M", "<strong>Up</strong>"],
["Users", "10K", "<em>Stable</em>"],
["Churn", "2%", "<mark>Down</mark>"],
]}
/>Code Blocks
With Language
tsx
<Code language="typescript">
{`function greet(name: string): string {
return \`Hello, \${name}!\`;
}
console.log(greet('World'));`}
</Code>Without Language
tsx
<Code>
{`Plain text code block
No syntax highlighting`}
</Code>JSON Block
tsx
<Json>
{{
user: { name: "Alice", tier: "premium" },
status: "active",
}}
</Json>Quotes & Citations
Blockquote
tsx
<Blockquote>
This is a quoted passage. It can span multiple lines and contain inline
formatting like <strong>bold</strong>.
</Blockquote>Inline Quote
tsx
<Paragraph>
As the saying goes, <quote>Practice makes perfect</quote>.
</Paragraph>Citation
tsx
<Paragraph>
According to <cite>Smith et al. (2023)</cite>, context matters.
</Paragraph>Multimodal Content
Images
tsx
// Native content block (always preserved)
<Image
source={{ type: 'url', url: 'https://example.com/photo.jpg' }}
altText="A beautiful sunset"
/>
// Inline semantic image (rendered to markdown/xml)
<img src="https://example.com/icon.png" alt="Icon" />Audio
tsx
<Audio source={{ type: "url", url: "https://example.com/audio.mp3" }} />Video
tsx
<Video source={{ type: "url", url: "https://example.com/video.mp4" }} />Documents
tsx
<Document
source={{ type: "url", url: "https://example.com/doc.pdf" }}
document_type="application/pdf"
/>Structural Elements
Section
Container for related content:
tsx
<Section id="user-profile" audience="model">
<H2>User Profile</H2>
<Paragraph>User information goes here.</Paragraph>
</Section>Audience options:
"model"- Only model sees it"user"- Only user sees it"all"- Both see it (default)
Grounding
Additional context that appears after system messages:
tsx
<Grounding position="after-system" audience="model">
<H3>Additional Context</H3>
<Paragraph>Background information for the model.</Paragraph>
</Grounding>Divider
Visual separator:
tsx
<Divider />Markdown: ---
XML: <hr />
Composition Patterns
Complex Sections
tsx
<Section audience="model">
<H2>Order Summary</H2>
<Table
headers={["Item", "Qty", "Price"]}
rows={order.items.map((item) => [
item.name,
String(item.quantity),
`$${item.price}`,
])}
/>
<Paragraph>
<strong>Total:</strong> ${order.total}
</Paragraph>
<List>
<ListItem>
Status: <mark>{order.status}</mark>
</ListItem>
<ListItem>Estimated delivery: {order.deliveryDate}</ListItem>
</List>
</Section>Conditional Content
tsx
<Section audience="model">
<H2>User Context</H2>
{user.isPremium && (
<>
<Paragraph>
Premium user: <strong>{user.name}</strong>
</Paragraph>
<List>
<ListItem>Priority support enabled</ListItem>
<ListItem>Advanced features unlocked</ListItem>
</List>
</>
)}
{!user.isPremium && <Paragraph>Standard user: {user.name}</Paragraph>}
</Section>Nested Structures
tsx
<Section audience="model">
<H1>Project Status</H1>
{projects.map((project) => (
<Section key={project.id}>
<H2>{project.name}</H2>
<Paragraph>{project.description}</Paragraph>
<H3>Tasks</H3>
<List ordered>
{project.tasks.map((task) => (
<ListItem key={task.id}>
{task.name}: <em>{task.status}</em>
</ListItem>
))}
</List>
</Section>
))}
</Section>Renderer Switching
Switch renderers for specific content using <Markdown> and <XML> components:
tsx
import { Markdown, XML } from "aidk";
<>
{/* Default renderer (from model) */}
<Section audience="model">
<H2>Standard Content</H2>
</Section>
{/* Force Markdown */}
<Markdown>
<Section audience="model">
<H2>Markdown-only Section</H2>
<Code language="python">print("Hello")</Code>
</Section>
</Markdown>
{/* Force XML */}
<XML>
<Section audience="model">
<H2>XML-only Section</H2>
<List>
<ListItem>Item</ListItem>
</List>
</Section>
</XML>
</>;Best Practices
1. Use Semantic Types
tsx
// ✅ Good: Semantic meaning clear
<Paragraph>
Price: <strong>$99.99</strong> (was <s>$149.99</s>)
</Paragraph>
// ❌ Less good: Meaning unclear
<Paragraph>
Price: **$99.99** (was ~~$149.99~~)
</Paragraph>2. Structure Content Logically
tsx
// ✅ Good: Clear hierarchy
<Section audience="model">
<H2>Main Topic</H2>
<Paragraph>Overview...</Paragraph>
<H3>Subtopic</H3>
<List>
<ListItem>Detail 1</ListItem>
<ListItem>Detail 2</ListItem>
</List>
</Section>3. Use Tables for Structured Data
tsx
// ✅ Good: Structured data in table
<Table
headers={['Metric', 'Value']}
rows={metrics.map(m => [m.name, m.value])}
/>
// ❌ Less good: Structured data as text
<Paragraph>
Metric 1: {value1}
Metric 2: {value2}
...
</Paragraph>4. Combine Primitives
tsx
// ✅ Good: Rich, formatted content
<Section audience="model">
<H2>API Response</H2>
<Paragraph>
Status: <mark>Success</mark> | Time: <inlineCode>{duration}ms</inlineCode>
</Paragraph>
<Code language="json">{JSON.stringify(response, null, 2)}</Code>
</Section>Testing
Test your components with both renderers:
tsx
import { MarkdownRenderer, XMLRenderer, Markdown, XML } from 'aidk';
import { render } from './test-utils';
describe('OrderSummary', () => {
it('renders as Markdown', () => {
const output = render(
<Markdown>
<OrderSummary order={testOrder} />
</Markdown>
);
expect(output).toContain('## Order Summary');
expect(output).toContain('| Item | Qty |');
});
it('renders as XML', () => {
const output = render(
<XML>
<OrderSummary order={testOrder} />
</XML>
);
expect(output).toContain('<h2>Order Summary</h2>');
expect(output).toContain('<table>');
});
});Related
- Renderers Guide - Renderer system deep dive
- Core Concepts - Understanding components
- Examples - See primitives in action
Next: Creating Tools