taw-ui
Domain Surface

IssueCard

Canonical issue/ticket surface for GitHub, Linear, Jira, and any work-item provider. One component, any data source — your app authenticates, fetches, and maps to the canonical schema.

Theme
State
vercel/next.jsvercel/next.js#58234Open

When using generateStaticParams with dynamic route segments in the App Router, the build fails with 'Error: Page changed from SSG to SSR'. This happens consistently when the route has nested dynamic segments.

bugapp-router
1y ago
timneutkens

Installation

Terminal
npx shadcn@latest add "https://taw-ui.com/r/issue-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 IssueCard schema inline.

GitHub mapping
// Your app fetches (auth is yours)
const { data: issue } = await octokit.issues.get({
  owner: "vercel", repo: "next.js", issue_number: 58234,
})

// Map to the IssueCard schema
const issueData = {
  id: `github:${issue.number}`,
  provider: "github",
  title: issue.title,
  number: issue.number,
  status: { label: issue.state, color: issue.state === "open" ? "green" : "red" },
  // ... map remaining fields
}
Linear mapping
// Your app fetches (auth is yours)
const issue = await linearClient.issue("issue-id")

// Map to the IssueCard schema
const issueData = {
  id: `linear:${issue.identifier}`,
  provider: "linear",
  title: issue.title,
  status: { label: issue.state.name, color: issue.state.color },
  priority: issue.priority === 1 ? "urgent" : "medium",
  // ... map remaining fields
}

Usage as AI Tool

IssueCard 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 { IssueCardSchema } from "@/components/taw/issue-card"

export const getIssue = tool({
  description: "Look up an issue",
  parameters: z.object({
    owner: z.string(),
    repo: z.string(),
    number: z.number(),
  }),
  outputSchema: IssueCardSchema,
  execute: async ({ owner, repo, number }) => {
    const issue = await fetchIssue(owner, repo, number)
    return {
      id: \`github:\${number}\`,
      provider: "github",
      title: issue.title,
      status: { label: issue.state },
      // ... map remaining fields
    }
  },
})
client — render
import { IssueCard } from "@/components/taw/issue-card"
import type { ToolPart } from "@/components/taw/lib/types"

function ToolOutput({ part }: { part: ToolPart }) {
  // Handles loading, error, and success states
  return <IssueCard part={part} />
}

Providers

Switch between fixtures to see IssueCard rendering data from different providers.

GitHub
vercel/next.jsvercel/next.js#58234Open

When using generateStaticParams with dynamic route segments in the App Router, the build fails with 'Error: Page changed from SSG to SSR'. This happens consistently when the route has nested dynamic segments.

bugapp-router
1y ago
timneutkens
Linear
PlatformENG-423In Progress

Webhook deliveries currently fail silently after the first attempt. We need to implement retry logic with exponential backoff (1s, 2s, 4s, 8s, 16s) and dead letter queue for failed deliveries.

backendreliability
1y ago
S

Props

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

Schema

IssueCardSchema
FieldTypeDescription
id*stringStable identifier (e.g. "github:owner/repo#42")
provider*"github" | "linear" | "jira" | "other"Source provider for icon and branding
title*stringIssue title
numbernumberIssue number (#123)
status*StatusNormalized status with optional color
priority"urgent" | "high" | "medium" | "low" | "none"Priority level — controls accent strip color
assigneeAssigneeAssigned person with optional avatar
labelsLabel[]Tags/labels with optional colors
projectstringProject or repository name
urlstring (URL)Link back to the issue in the provider
createdAtstringCreation timestamp (ISO 8601 or relative)
updatedAtstringLast update timestamp
descriptionstringIssue body (truncated for display)
confidencenumber (0-1)AI confidence in this data
caveatstringUncertainty note
sourceSourceData provenance
Status
FieldTypeDescription
label*stringDisplay text (e.g. "Open", "In Progress")
colorstringCSS color for the status badge
Assignee
FieldTypeDescription
name*stringDisplay name or username
avatarUrlstring (URL)Avatar image URL
Label
FieldTypeDescription
name*stringLabel text
colorstringCSS color for the label chip

Data Mapping

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

GitHub → IssueCard
// Map GitHub REST API fields
const issueData = {
  id: `github:${issue.number}`,
  provider: "github",
  title: issue.title,
  number: issue.number,
  status: { label: issue.state },
  assignee: issue.assignee
    ? { name: issue.assignee.login }
    : undefined,
}
Linear → IssueCard
// Map Linear GraphQL fields
const issueData = {
  id: `linear:${issue.identifier}`,
  provider: "linear",
  title: issue.title,
  status: { label: issue.state.name,
    color: issue.state.color },
  priority: issue.priority === 1
    ? "urgent" : "medium",
}

Features

Provider icons

Native SVG icons for GitHub, Linear, Jira — plus a generic fallback

Priority accent strip

Left border colored by priority level for instant visual signal

Status + label badges

Color-coded status badge and label chips with provider colors

Rich metadata

Assignee avatar, relative timestamps, project name, issue number

Graceful degradation

Renders beautifully from minimal (4 fields) to fully populated data

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, access tokens, refresh tokens, API clients, 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