📄 ai-sdk/docs/ai-sdk-ui/message-metadata

File: message-metadata.md | Updated: 11/15/2025

Source: https://ai-sdk.dev/docs/ai-sdk-ui/message-metadata

AI SDK

Menu

v5 (Latest)

AI SDK 5.x

AI SDK by Vercel

AI SDK 6 Beta

Foundations

Overview

Providers and Models

Prompts

Tools

Streaming

Getting Started

Navigating the Library

Next.js App Router

Next.js Pages Router

Svelte

Vue.js (Nuxt)

Node.js

Expo

Agents

Agents

Building Agents

Workflow Patterns

Loop Control

AI SDK Core

Overview

Generating Text

Generating Structured Data

Tool Calling

Model Context Protocol (MCP) Tools

Prompt Engineering

Settings

Embeddings

Image Generation

Transcription

Speech

Language Model Middleware

Provider & Model Management

Error Handling

Testing

Telemetry

AI SDK UI

Overview

Chatbot

Chatbot Message Persistence

Chatbot Resume Streams

Chatbot Tool Usage

Generative User Interfaces

Completion

Object Generation

Streaming Custom Data

Error Handling

Transport

Reading UIMessage Streams

Message Metadata

Stream Protocols

AI SDK RSC

Advanced

Reference

AI SDK Core

AI SDK UI

AI SDK RSC

Stream Helpers

AI SDK Errors

Migration Guides

Troubleshooting

Copy markdown

Message Metadata

========================================================================================

Message metadata allows you to attach custom information to messages at the message level. This is useful for tracking timestamps, model information, token usage, user context, and other message-level data.

Overview


Message metadata differs from data parts in that it's attached at the message level rather than being part of the message content. While data parts are ideal for dynamic content that forms part of the message, metadata is perfect for information about the message itself.

Getting Started


Here's a simple example of using message metadata to track timestamps and model information:

Defining Metadata Types

First, define your metadata type for type safety:

app/types.ts

import { UIMessage } from 'ai';import { z } from 'zod';
// Define your metadata schemaexport const messageMetadataSchema = z.object({  createdAt: z.number().optional(),  model: z.string().optional(),  totalTokens: z.number().optional(),});
export type MessageMetadata = z.infer<typeof messageMetadataSchema>;
// Create a typed UIMessageexport type MyUIMessage = UIMessage<MessageMetadata>;

Sending Metadata from the Server

Use the messageMetadata callback in toUIMessageStreamResponse to send metadata at different streaming stages:

app/api/chat/route.ts

import { openai } from '@ai-sdk/openai';import { convertToModelMessages, streamText } from 'ai';import type { MyUIMessage } from '@/types';
export async function POST(req: Request) {  const { messages }: { messages: MyUIMessage[] } = await req.json();
  const result = streamText({    model: openai('gpt-4o'),    messages: convertToModelMessages(messages),  });
  return result.toUIMessageStreamResponse({    originalMessages: messages, // pass this in for type-safe return objects    messageMetadata: ({ part }) => {      // Send metadata when streaming starts      if (part.type === 'start') {        return {          createdAt: Date.now(),          model: 'gpt-4o',        };      }
      // Send additional metadata when streaming completes      if (part.type === 'finish') {        return {          totalTokens: part.totalUsage.totalTokens,        };      }    },  });}

To enable type-safe metadata return object in messageMetadata, pass in the originalMessages parameter typed to your UIMessage type.

Accessing Metadata on the Client

Access metadata through the message.metadata property:

app/page.tsx

'use client';
import { useChat } from '@ai-sdk/react';import { DefaultChatTransport } from 'ai';import type { MyUIMessage } from '@/types';
export default function Chat() {  const { messages } = useChat<MyUIMessage>({    transport: new DefaultChatTransport({      api: '/api/chat',    }),  });
  return (    <div>      {messages.map(message => (        <div key={message.id}>          <div>            {message.role === 'user' ? 'User: ' : 'AI: '}            {message.metadata?.createdAt && (              <span className="text-sm text-gray-500">                {new Date(message.metadata.createdAt).toLocaleTimeString()}              </span>            )}          </div>
          {/* Render message content */}          {message.parts.map((part, index) =>            part.type === 'text' ? <div key={index}>{part.text}</div> : null,          )}
          {/* Display additional metadata */}          {message.metadata?.totalTokens && (            <div className="text-xs text-gray-400">              {message.metadata.totalTokens} tokens            </div>          )}        </div>      ))}    </div>  );}

For streaming arbitrary data that changes during generation, consider using data parts instead.

Common Use Cases


Message metadata is ideal for:

  • Timestamps: When messages were created or completed
  • Model Information: Which AI model was used
  • Token Usage: Track costs and usage limits
  • User Context: User IDs, session information
  • Performance Metrics: Generation time, time to first token
  • Quality Indicators: Finish reason, confidence scores

See Also


On this page

Message Metadata

Overview

Getting Started

Defining Metadata Types

Sending Metadata from the Server

Accessing Metadata on the Client

Common Use Cases

See Also

Deploy and Scale AI Apps with Vercel.

Vercel delivers the infrastructure and developer experience you need to ship reliable AI-powered applications at scale.

Trusted by industry leaders:

  • OpenAI
  • Photoroom
  • leonardo-ai Logoleonardo-ai Logo
  • zapier Logozapier Logo

Talk to an expert