PostCard
Canonical social/content post surface for X, Instagram, LinkedIn, Threads, and any content provider. One component, any data source — your app authenticates, fetches, and maps to the canonical schema.
Installation
npx shadcn@latest add "https://taw-ui.com/r/post-card.json"This copies the component source and schema into your project. You own the code — customize anything.
Usage with Data Mapping
The recommended pattern: your app fetches data from the provider and maps it to the PostCard schema inline.
// Your app fetches (auth is yours)
const { data: tweet } = await xClient.tweets
.findTweetById("1234567890")
// Map to the PostCard schema
const postData = {
id: `x:${tweet.id}`,
provider: "x",
author: { name: tweet.author.name,
handle: `@${tweet.author.username}` },
body: tweet.text,
postedAt: tweet.created_at,
// ... map remaining fields
}// Your app fetches (auth is yours)
const post = await ig.get(`/${postId}`, {
fields: "id,caption,timestamp,..."
})
// Map to the PostCard schema
const postData = {
id: `instagram:${post.id}`,
provider: "instagram",
author: { name: post.username },
body: post.caption,
postedAt: post.timestamp,
// ... map remaining fields
}Usage as AI Tool
PostCard also works as a standard taw-ui tool output — let the AI populate the canonical schema directly.
import { tool } from "ai"
import { PostCardSchema } from "@/components/taw/post-card"
export const getPost = tool({
description: "Find a social media post",
parameters: z.object({
query: z.string(),
}),
outputSchema: PostCardSchema,
execute: async ({ query }) => {
const post = await searchPosts(query)
return {
id: \`x:\${post.id}\`,
provider: "x",
author: { name: post.author.name },
body: post.text,
postedAt: post.created_at,
// ... map remaining fields
}
},
})import { PostCard } from "@/components/taw/post-card"
import type { ToolPart } from "@/components/taw/lib/types"
function ToolOutput({ part }: { part: ToolPart }) {
// Handles loading, error, and success states
return <PostCard part={part} />
}Providers
Switch between fixtures to see PostCard rendering data from different providers.
Props
| Field | Type | Description |
|---|---|---|
| part* | ToolPart | Tool call lifecycle state — handles loading, error, and success |
| animate | boolean | Enable entrance animations (default: true) |
| className | string | Additional CSS classes on the wrapper |
Schema
| Field | Type | Description |
|---|---|---|
| id* | string | Stable identifier (e.g. "x:1234567890") |
| provider* | "x" | "instagram" | "linkedin" | "threads" | "other" | Source provider for icon and branding |
| author* | Author | Post author |
| body* | string | Post text / caption / body |
| postedAt* | string | Publication timestamp (ISO 8601) |
| media | Media[] | Image or video attachments |
| metrics | Metrics | Engagement metrics (likes, comments, etc.) |
| url | string (URL) | Link back to the post in the provider |
| status | "published" | "draft" | "deleted" | Post status |
| tags | string[] | Hashtags or topic tags |
| confidence | number (0-1) | AI confidence in this data |
| caveat | string | Uncertainty note |
| source | Source | Data provenance |
| Field | Type | Description |
|---|---|---|
| name* | string | Display name |
| handle | string | Username / handle (e.g. "@elonmusk") |
| avatarUrl | string (URL) | Avatar image URL |
| url | string (URL) | Profile URL |
| isVerified | boolean | Whether the author is verified |
| Field | Type | Description |
|---|---|---|
| type* | "image" | "video" | Media type |
| url* | string (URL) | Media URL |
| alt | string | Alt text for accessibility |
| width | number | Intrinsic width in pixels |
| height | number | Intrinsic height in pixels |
| Field | Type | Description |
|---|---|---|
| likes | number | Like / favorite count |
| comments | number | Comment / reply count |
| reposts | number | Repost / share / retweet count |
| views | number | View / impression count |
Data Mapping
Map your provider's API response to the PostCardSchema inline. No special adapters needed — just a plain object mapping.
// Map X API v2 fields
const postData = {
id: `x:${tweet.id}`,
provider: "x",
author: { name: tweet.author.name,
handle: `@${tweet.author.username}`,
isVerified: tweet.author.verified },
body: tweet.text,
postedAt: tweet.created_at,
metrics: { likes: tweet.public_metrics.like_count },
}// Map Instagram Graph API fields
const postData = {
id: `instagram:${post.id}`,
provider: "instagram",
author: { name: post.username },
body: post.caption,
postedAt: post.timestamp,
media: post.media_url
? [{ type: "image", url: post.media_url }]
: [],
}Features
Native SVG icons for X, Instagram, LinkedIn, Threads — plus a generic fallback
Left border branded by provider: black for X, gradient for Instagram, blue for LinkedIn
Avatar, display name, handle, and verified badge — all provider-agnostic
Likes, comments, reposts, and views with compact number formatting (1.2K, 3.4M)
Single images, multi-image grids, and video thumbnails with play overlay
Map any provider's API response to the canonical schema — no adapters needed
Auth Boundary
taw-ui does not handle authentication. OAuth flows, API keys, bearer tokens, and data fetching are the responsibility of your application. taw-ui provides schemas, components, and validation — nothing more.
See Domain Surfaces for the full architecture explanation.