@v0-sdk/react
Version:
Headless React components for rendering v0 Platform API content
595 lines (582 loc) • 21.6 kB
TypeScript
import React$1 from 'react';
/**
* Binary format for message content as returned by the v0 Platform API
* Each row is a tuple where the first element is the type and the rest are data
*/
type MessageBinaryFormat = [number, ...any[]][];
/**
* Individual row in the message binary format
*/
type MessageBinaryFormatRow = MessageBinaryFormat[number];
/**
* Props for the Message component
*/
interface MessageProps {
/**
* The parsed content from the v0 Platform API
* This should be the JSON.parsed value of the 'content' field from API responses
*/
content: MessageBinaryFormat;
/**
* Optional message ID for tracking purposes
*/
messageId?: string;
/**
* Role of the message sender
*/
role?: 'user' | 'assistant' | 'system' | 'tool';
/**
* Whether the message is currently being streamed
*/
streaming?: boolean;
/**
* Whether this is the last message in the conversation
*/
isLastMessage?: boolean;
/**
* Custom className for styling the root container
*/
className?: string;
/**
* Custom component renderers (react-markdown style)
* Override specific components by name
* Can be either a React component or an object with className for simple styling
*/
components?: {
CodeBlock?: React.ComponentType<{
language: string;
code: string;
className?: string;
}>;
MathPart?: React.ComponentType<{
content: string;
inline?: boolean;
className?: string;
}>;
CodeProjectPart?: React.ComponentType<{
title?: string;
filename?: string;
code?: string;
language?: string;
collapsed?: boolean;
className?: string;
}>;
ThinkingSection?: React.ComponentType<{
title?: string;
duration?: number;
thought?: string;
collapsed?: boolean;
onCollapse?: () => void;
className?: string;
children?: React.ReactNode;
brainIcon?: React.ReactNode;
chevronRightIcon?: React.ReactNode;
chevronDownIcon?: React.ReactNode;
}>;
TaskSection?: React.ComponentType<{
title?: string;
type?: string;
parts?: any[];
collapsed?: boolean;
onCollapse?: () => void;
className?: string;
children?: React.ReactNode;
taskIcon?: React.ReactNode;
chevronRightIcon?: React.ReactNode;
chevronDownIcon?: React.ReactNode;
}>;
Icon?: React.ComponentType<{
name: 'chevron-right' | 'chevron-down' | 'search' | 'folder' | 'settings' | 'file-text' | 'brain' | 'wrench';
className?: string;
}>;
p?: React.ComponentType<React.HTMLAttributes<HTMLParagraphElement>> | {
className?: string;
};
h1?: React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>> | {
className?: string;
};
h2?: React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>> | {
className?: string;
};
h3?: React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>> | {
className?: string;
};
h4?: React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>> | {
className?: string;
};
h5?: React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>> | {
className?: string;
};
h6?: React.ComponentType<React.HTMLAttributes<HTMLHeadingElement>> | {
className?: string;
};
ul?: React.ComponentType<React.HTMLAttributes<HTMLUListElement>> | {
className?: string;
};
ol?: React.ComponentType<React.HTMLAttributes<HTMLOListElement>> | {
className?: string;
};
li?: React.ComponentType<React.HTMLAttributes<HTMLLIElement>> | {
className?: string;
};
blockquote?: React.ComponentType<React.HTMLAttributes<HTMLQuoteElement>> | {
className?: string;
};
code?: React.ComponentType<React.HTMLAttributes<HTMLElement>> | {
className?: string;
};
pre?: React.ComponentType<React.HTMLAttributes<HTMLPreElement>> | {
className?: string;
};
strong?: React.ComponentType<React.HTMLAttributes<HTMLElement>> | {
className?: string;
};
em?: React.ComponentType<React.HTMLAttributes<HTMLElement>> | {
className?: string;
};
a?: React.ComponentType<React.AnchorHTMLAttributes<HTMLAnchorElement>> | {
className?: string;
};
hr?: React.ComponentType<React.HTMLAttributes<HTMLHRElement>> | {
className?: string;
};
div?: React.ComponentType<React.HTMLAttributes<HTMLDivElement>> | {
className?: string;
};
span?: React.ComponentType<React.HTMLAttributes<HTMLSpanElement>> | {
className?: string;
};
};
/**
* @deprecated Use `components` instead. Will be removed in next major version.
*/
renderers?: {
CodeBlock?: React.ComponentType<{
language: string;
code: string;
className?: string;
}>;
MathRenderer?: React.ComponentType<{
content: string;
inline?: boolean;
className?: string;
}>;
MathPart?: React.ComponentType<{
content: string;
inline?: boolean;
className?: string;
}>;
Icon?: React.ComponentType<{
name: 'chevron-right' | 'chevron-down' | 'search' | 'folder' | 'settings' | 'file-text' | 'brain' | 'wrench';
className?: string;
}>;
};
}
type MessageRendererProps = MessageProps;
type V0MessageRendererProps = MessageProps;
interface MessageData {
elements: MessageElement[];
messageId: string;
role: string;
streaming: boolean;
isLastMessage: boolean;
}
interface MessageElement {
type: 'text' | 'html' | 'component' | 'content-part' | 'code-project';
key: string;
data: any;
props?: Record<string, any>;
children?: MessageElement[];
}
declare function useMessage({ content, messageId, role, streaming, isLastMessage, components, renderers, }: Omit<MessageProps, 'className'>): MessageData;
declare function MessageImpl({ content, messageId, role, streaming, isLastMessage, className, components, renderers, }: MessageProps): React$1.FunctionComponentElement<{
messageData: MessageData;
className?: string;
}>;
/**
* Main component for rendering v0 Platform API message content
* This is a backward-compatible JSX renderer. For headless usage, use the useMessage hook.
*/
declare const Message: React$1.MemoExoticComponent<typeof MessageImpl>;
interface StreamingMessageState {
content: MessageBinaryFormat;
isStreaming: boolean;
error?: string;
isComplete: boolean;
}
interface UseStreamingMessageOptions {
onChunk?: (chunk: MessageBinaryFormat) => void;
onComplete?: (finalContent: MessageBinaryFormat) => void;
onError?: (error: string) => void;
onChatData?: (chatData: any) => void;
}
/**
* Hook for handling streaming message content from v0 API using useSyncExternalStore
*/
declare function useStreamingMessage(stream: ReadableStream<Uint8Array> | null, options?: UseStreamingMessageOptions): StreamingMessageState;
interface StreamingMessageProps extends Omit<MessageProps, 'content' | 'streaming' | 'isLastMessage'>, UseStreamingMessageOptions {
/**
* The streaming response from v0.chats.create() with responseMode: 'experimental_stream'
*/
stream: ReadableStream<Uint8Array> | null;
/**
* Show a loading indicator while no content has been received yet
*/
showLoadingIndicator?: boolean;
/**
* Custom loading component
*/
loadingComponent?: React$1.ReactNode;
/**
* Custom error component
*/
errorComponent?: (error: string) => React$1.ReactNode;
}
interface StreamingMessageData extends StreamingMessageState {
messageData: MessageData | null;
}
declare function useStreamingMessageData({ stream, messageId, role, components, renderers, onChunk, onComplete, onError, onChatData, }: Omit<StreamingMessageProps, 'className' | 'showLoadingIndicator' | 'loadingComponent' | 'errorComponent'>): StreamingMessageData;
/**
* Component for rendering streaming message content from v0 API
*
* For headless usage, use the useStreamingMessageData hook instead.
*
* @example
* ```tsx
* import { v0 } from 'v0-sdk'
* import { StreamingMessage } from '@v0-sdk/react'
*
* function ChatDemo() {
* const [stream, setStream] = useState<ReadableStream<Uint8Array> | null>(null)
*
* const handleSubmit = async () => {
* const response = await v0.chats.create({
* message: 'Create a button component',
* responseMode: 'experimental_stream'
* })
* setStream(response)
* }
*
* return (
* <div>
* <button onClick={handleSubmit}>Send Message</button>
* {stream && (
* <StreamingMessage
* stream={stream}
* messageId="demo-message"
* role="assistant"
* onComplete={(content) => handleCompletion(content)}
* onChatData={(chatData) => handleChatData(chatData)}
* />
* )}
* </div>
* )
* }
* ```
*/
declare function StreamingMessage({ stream, showLoadingIndicator, loadingComponent, errorComponent, onChunk, onComplete, onError, onChatData, className, ...messageProps }: StreamingMessageProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
className: string;
style: {
color: "red";
padding: string;
border: string;
borderRadius: string;
};
}, HTMLElement> | React$1.DetailedReactHTMLElement<{
className: string;
style: {
display: "flex";
alignItems: "center";
gap: string;
color: "#6b7280";
};
}, HTMLElement> | React$1.FunctionComponentElement<MessageProps>;
interface IconProps {
name: 'chevron-right' | 'chevron-down' | 'search' | 'folder' | 'settings' | 'file-text' | 'brain' | 'wrench';
className?: string;
}
interface IconData {
name: IconProps['name'];
fallback: string;
ariaLabel: string;
}
declare function useIcon(props: IconProps): IconData;
/**
* Generic icon component that can be customized by consumers.
* By default, renders a simple fallback. Consumers should provide
* their own icon implementation via context or props.
*
* For headless usage, use the useIcon hook instead.
*/
declare function Icon(props: IconProps): React$1.ReactElement<IconProps, string | React$1.JSXElementConstructor<any>> | React$1.DetailedReactHTMLElement<{
className: string;
'data-icon': "search" | "brain" | "chevron-right" | "chevron-down" | "folder" | "settings" | "file-text" | "wrench";
'aria-label': string;
}, HTMLElement>;
/**
* Provider for custom icon implementation
*/
declare function IconProvider({ children, component, }: {
children: React$1.ReactNode;
component: React$1.ComponentType<IconProps>;
}): React$1.FunctionComponentElement<React$1.ProviderProps<React$1.ComponentType<IconProps>>>;
interface ThinkingSectionProps {
title?: string;
duration?: number;
thought?: string;
collapsed?: boolean;
onCollapse?: () => void;
className?: string;
children?: React$1.ReactNode;
iconRenderer?: React$1.ComponentType<IconProps>;
brainIcon?: React$1.ReactNode;
chevronRightIcon?: React$1.ReactNode;
chevronDownIcon?: React$1.ReactNode;
}
interface ThinkingSectionData {
title: string;
duration?: number;
thought?: string;
collapsed: boolean;
paragraphs: string[];
formattedDuration?: string;
}
declare function useThinkingSection({ title, duration, thought, collapsed: initialCollapsed, onCollapse, }: Omit<ThinkingSectionProps, 'className' | 'children' | 'iconRenderer' | 'brainIcon' | 'chevronRightIcon' | 'chevronDownIcon'>): {
data: ThinkingSectionData;
collapsed: boolean;
handleCollapse: () => void;
};
/**
* Generic thinking section component
* Renders a collapsible section with basic structure - consumers provide styling
*
* For headless usage, use the useThinkingSection hook instead.
*/
declare function ThinkingSection({ title, duration, thought, collapsed: initialCollapsed, onCollapse, className, children, iconRenderer, brainIcon, chevronRightIcon, chevronDownIcon, }: ThinkingSectionProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
className: string;
'data-component': string;
}, HTMLElement>;
interface TaskSectionProps {
title?: string;
type?: string;
parts?: any[];
collapsed?: boolean;
onCollapse?: () => void;
className?: string;
children?: React$1.ReactNode;
iconRenderer?: React$1.ComponentType<IconProps>;
taskIcon?: React$1.ReactNode;
chevronRightIcon?: React$1.ReactNode;
chevronDownIcon?: React$1.ReactNode;
}
interface TaskSectionData {
title: string;
type?: string;
parts: any[];
collapsed: boolean;
meaningfulParts: any[];
shouldShowCollapsible: boolean;
iconName: IconProps['name'];
}
interface TaskPartData {
type: string;
status?: string;
content: React$1.ReactNode;
isSearching?: boolean;
isAnalyzing?: boolean;
isComplete?: boolean;
query?: string;
count?: number;
answer?: string;
sources?: Array<{
url: string;
title: string;
}>;
files?: string[];
issues?: number;
}
declare function useTaskSection({ title, type, parts, collapsed: initialCollapsed, onCollapse, }: Omit<TaskSectionProps, 'className' | 'children' | 'iconRenderer' | 'taskIcon' | 'chevronRightIcon' | 'chevronDownIcon'>): {
data: TaskSectionData;
collapsed: boolean;
handleCollapse: () => void;
processedParts: TaskPartData[];
};
/**
* Generic task section component
* Renders a collapsible task section with basic structure - consumers provide styling
*
* For headless usage, use the useTaskSection hook instead.
*/
declare function TaskSection({ title, type, parts, collapsed: initialCollapsed, onCollapse, className, children, iconRenderer, taskIcon, chevronRightIcon, chevronDownIcon, }: TaskSectionProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
className: string;
'data-component': string;
}, HTMLElement>;
interface CodeProjectPartProps {
title?: string;
filename?: string;
code?: string;
language?: string;
collapsed?: boolean;
className?: string;
children?: React$1.ReactNode;
iconRenderer?: React$1.ComponentType<IconProps>;
}
interface CodeProjectData {
title: string;
filename?: string;
code?: string;
language: string;
collapsed: boolean;
files: Array<{
name: string;
path: string;
active: boolean;
}>;
}
declare function useCodeProject({ title, filename, code, language, collapsed: initialCollapsed, }: Omit<CodeProjectPartProps, 'className' | 'children' | 'iconRenderer'>): {
data: CodeProjectData;
collapsed: boolean;
toggleCollapsed: () => void;
};
/**
* Generic code project block component
* Renders a collapsible code project with basic structure - consumers provide styling
*
* For headless usage, use the useCodeProject hook instead.
*/
declare function CodeProjectPart({ title, filename, code, language, collapsed: initialCollapsed, className, children, iconRenderer, }: CodeProjectPartProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
className: string;
'data-component': string;
}, HTMLElement>;
interface ContentPartRendererProps {
part: any;
iconRenderer?: React$1.ComponentType<IconProps>;
thinkingSectionRenderer?: React$1.ComponentType<{
title?: string;
duration?: number;
thought?: string;
collapsed?: boolean;
onCollapse?: () => void;
className?: string;
children?: React$1.ReactNode;
brainIcon?: React$1.ReactNode;
chevronRightIcon?: React$1.ReactNode;
chevronDownIcon?: React$1.ReactNode;
}>;
taskSectionRenderer?: React$1.ComponentType<{
title?: string;
type?: string;
parts?: any[];
collapsed?: boolean;
onCollapse?: () => void;
className?: string;
children?: React$1.ReactNode;
taskIcon?: React$1.ReactNode;
chevronRightIcon?: React$1.ReactNode;
chevronDownIcon?: React$1.ReactNode;
}>;
brainIcon?: React$1.ReactNode;
chevronRightIcon?: React$1.ReactNode;
chevronDownIcon?: React$1.ReactNode;
searchIcon?: React$1.ReactNode;
folderIcon?: React$1.ReactNode;
settingsIcon?: React$1.ReactNode;
wrenchIcon?: React$1.ReactNode;
}
interface ContentPartData {
type: string;
parts: any[];
metadata: Record<string, any>;
componentType: 'thinking' | 'task' | 'unknown' | null;
title?: string;
iconName?: IconProps['name'];
thinkingData?: {
duration?: number;
thought?: string;
};
}
declare function useContentPart(part: any): ContentPartData;
/**
* Content part renderer that handles different types of v0 API content parts
*
* For headless usage, use the useContentPart hook instead.
*/
declare function ContentPartRenderer({ part, iconRenderer, thinkingSectionRenderer, taskSectionRenderer, brainIcon, chevronRightIcon, chevronDownIcon, searchIcon, folderIcon, settingsIcon, wrenchIcon, }: ContentPartRendererProps): React$1.ReactElement<{
title?: string;
duration?: number;
thought?: string;
collapsed?: boolean;
onCollapse?: () => void;
className?: string;
children?: React$1.ReactNode;
brainIcon?: React$1.ReactNode;
chevronRightIcon?: React$1.ReactNode;
chevronDownIcon?: React$1.ReactNode;
}, string | React$1.JSXElementConstructor<any>> | React$1.ReactElement<{
title?: string;
type?: string;
parts?: any[];
collapsed?: boolean;
onCollapse?: () => void;
className?: string;
children?: React$1.ReactNode;
taskIcon?: React$1.ReactNode;
chevronRightIcon?: React$1.ReactNode;
chevronDownIcon?: React$1.ReactNode;
}, string | React$1.JSXElementConstructor<any>> | React$1.ReactElement<{
'data-unknown-part-type': string;
}, string | React$1.JSXElementConstructor<any>>;
interface MathPartProps {
content: string;
inline?: boolean;
className?: string;
children?: React$1.ReactNode;
displayMode?: boolean;
}
interface MathData {
content: string;
inline: boolean;
displayMode: boolean;
processedContent: string;
}
declare function useMath(props: Omit<MathPartProps, 'className' | 'children'>): MathData;
/**
* Generic math renderer component
* Renders plain math content by default - consumers should provide their own math rendering
*
* For headless usage, use the useMath hook instead.
*/
declare function MathPart({ content, inline, className, children, displayMode, }: MathPartProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
className: string;
'data-math-inline': boolean;
'data-math-display': boolean;
}, HTMLElement>;
interface CodeBlockProps {
language: string;
code: string;
className?: string;
children?: React$1.ReactNode;
filename?: string;
}
interface CodeBlockData {
language: string;
code: string;
filename?: string;
lines: string[];
lineCount: number;
}
declare function useCodeBlock(props: Omit<CodeBlockProps, 'className' | 'children'>): CodeBlockData;
/**
* Generic code block component
* Renders plain code by default - consumers should provide their own styling and highlighting
*
* For headless usage, use the useCodeBlock hook instead.
*/
declare function CodeBlock({ language, code, className, children, filename, }: CodeBlockProps): React$1.FunctionComponentElement<React$1.FragmentProps> | React$1.DetailedReactHTMLElement<{
'data-filename': string;
className: string;
'data-language': string;
}, HTMLElement>;
export { ContentPartRenderer as AssistantMessageContentPart, CodeBlock, CodeProjectPart as CodeProjectBlock, CodeProjectPart, ContentPartRenderer, Icon, IconProvider, MathPart, MathPart as MathRenderer, Message, Message as MessageContent, Message as MessageRenderer, StreamingMessage, TaskSection, ThinkingSection, Message as V0MessageRenderer, useCodeBlock, useCodeProject, useContentPart, useIcon, useMath, useMessage, useStreamingMessage, useStreamingMessageData, useTaskSection, useThinkingSection };
export type { CodeBlockData, CodeBlockProps, CodeProjectPartProps as CodeProjectBlockProps, CodeProjectData, CodeProjectPartProps, ContentPartData, ContentPartRendererProps, IconData, IconProps, MathData, MathPartProps, MathPartProps as MathRendererProps, MessageBinaryFormat, MessageBinaryFormatRow, MessageData, MessageElement, MessageProps, MessageRendererProps, StreamingMessageData, StreamingMessageProps, StreamingMessageState, TaskPartData, TaskSectionData, TaskSectionProps, ThinkingSectionData, ThinkingSectionProps, UseStreamingMessageOptions, V0MessageRendererProps };
//# sourceMappingURL=index.d.ts.map