taw-ui
Domain Surface

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.

Theme
State

Installation

Terminal
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.

X mapping
// 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
}
Instagram mapping
// 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.

server — define tool
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
    }
  },
})
client — render
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.

X
Instagram

Props

FieldTypeDescription
part*ToolPartTool call lifecycle state — handles loading, error, and success
animatebooleanEnable entrance animations (default: true)
classNamestringAdditional CSS classes on the wrapper

Schema

PostCardSchema
FieldTypeDescription
id*stringStable identifier (e.g. "x:1234567890")
provider*"x" | "instagram" | "linkedin" | "threads" | "other"Source provider for icon and branding
author*AuthorPost author
body*stringPost text / caption / body
postedAt*stringPublication timestamp (ISO 8601)
mediaMedia[]Image or video attachments
metricsMetricsEngagement metrics (likes, comments, etc.)
urlstring (URL)Link back to the post in the provider
status"published" | "draft" | "deleted"Post status
tagsstring[]Hashtags or topic tags
confidencenumber (0-1)AI confidence in this data
caveatstringUncertainty note
sourceSourceData provenance
Author
FieldTypeDescription
name*stringDisplay name
handlestringUsername / handle (e.g. "@elonmusk")
avatarUrlstring (URL)Avatar image URL
urlstring (URL)Profile URL
isVerifiedbooleanWhether the author is verified
Media
FieldTypeDescription
type*"image" | "video"Media type
url*string (URL)Media URL
altstringAlt text for accessibility
widthnumberIntrinsic width in pixels
heightnumberIntrinsic height in pixels
Metrics
FieldTypeDescription
likesnumberLike / favorite count
commentsnumberComment / reply count
repostsnumberRepost / share / retweet count
viewsnumberView / impression count

Data Mapping

Map your provider's API response to the PostCardSchema inline. No special adapters needed — just a plain object mapping.

X → PostCard
// 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 },
}
Instagram → PostCard
// 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

Provider icons

Native SVG icons for X, Instagram, LinkedIn, Threads — plus a generic fallback

Provider accent strip

Left border branded by provider: black for X, gradient for Instagram, blue for LinkedIn

Author identity

Avatar, display name, handle, and verified badge — all provider-agnostic

Engagement metrics

Likes, comments, reposts, and views with compact number formatting (1.2K, 3.4M)

Media support

Single images, multi-image grids, and video thumbnails with play overlay

Inline data mapping

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.

Related