Documentation Index Fetch the complete documentation index at: https://mintlify.com/ephraimduncan/blocks/llms.txt
Use this file to discover all available pages before exploring further.
TypeScript Support
Blocks is built with TypeScript and provides comprehensive type definitions for all components and utilities.
Component Props Types
All components extend native HTML element types using React.ComponentProps:
import * as React from "react"
function Input ({ className , type , ... props } : React . ComponentProps < "input" >) {
return < input type = { type } className = { className } { ... props } />
}
This provides:
Full autocomplete for native HTML attributes
Type safety for event handlers
Proper typing for refs
Variant Props with CVA
Components use class-variance-authority for type-safe variants:
VariantProps automatically infers the correct types from your variant definitions.
Utility Function Types
The cn() utility function is properly typed using clsx:
import { clsx , type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn ( ... inputs : ClassValue []) {
return twMerge ( clsx ( inputs ))
}
Usage with type safety:
import { cn } from "@/lib/utils"
// All valid ClassValue types
cn ( "base-class" )
cn ( "class-1" , "class-2" )
cn ({ "conditional-class" : true })
cn ([ "array" , "of" , "classes" ])
cn ( undefined , null , "class" ) // Handles falsy values
Component Composition Types
Composite components like Card have proper type exports:
Define sub-components
import * as React from "react"
import { cn } from "@/lib/utils"
function Card ({ className , ... props } : React . ComponentProps < "div" >) {
return (
< div
className = { cn ( "rounded-xl border shadow-sm" , className ) }
{ ... props }
/>
)
}
function CardHeader ({ className , ... props } : React . ComponentProps < "div" >) {
return (
< div
className = { cn ( "grid gap-2 p-6" , className ) }
{ ... props }
/>
)
}
function CardTitle ({ className , ... props } : React . ComponentProps < "div" >) {
return (
< div
className = { cn ( "font-semibold leading-none" , className ) }
{ ... props }
/>
)
}
Export all components
export {
Card ,
CardHeader ,
CardTitle ,
CardDescription ,
CardContent ,
CardFooter ,
}
Use with full type safety
import { Card , CardHeader , CardTitle , CardContent } from "@/components/ui/card"
export default function TypedCard () {
return (
< Card >
< CardHeader >
< CardTitle > Fully Typed </ CardTitle >
</ CardHeader >
< CardContent >
All props are type-checked
</ CardContent >
</ Card >
)
}
Complex Component Types
For complex components with multiple types, define interfaces:
import type { ReactNode } from "react"
import type { SupportedLanguages } from "@pierre/diffs/react"
interface BlockViewState {
view : "preview" | "code"
size : "desktop" | "tablet" | "mobile"
}
interface BaseItem {
name : string
path : string
}
interface FileItem extends BaseItem {
type : "file"
content : string
}
interface FolderItem extends BaseItem {
type : "folder"
children : FileTreeItem []
}
type FileTreeItem = FileItem | FolderItem
interface BlocksProps {
name : string
code ?: string | ReactNode
fileTree ?: FileTreeItem []
blocksId : string
blocksCategory : string
meta ?: {
iframeHeight ?: string
type ?: "file" | "directory"
}
}
Generic Component Types
Create reusable generic components with proper typing:
import type { ReactNode } from "react"
interface ListProps < T > {
items : T []
renderItem : ( item : T , index : number ) => ReactNode
keyExtractor : ( item : T , index : number ) => string
className ?: string
}
function List < T >({
items ,
renderItem ,
keyExtractor ,
className ,
} : ListProps < T >) {
return (
< ul className = { className } >
{ items . map (( item , index ) => (
< li key = { keyExtractor ( item , index ) } >
{ renderItem ( item , index ) }
</ li >
)) }
</ ul >
)
}
// Usage with type inference
interface User {
id : string
name : string
}
const users : User [] = [
{ id: "1" , name: "Alice" },
{ id: "2" , name: "Bob" },
]
< List
items = { users }
renderItem = { ( user ) => < span > { user . name } </ span > }
keyExtractor = { ( user ) => user . id }
/>
Event Handler Types
Properly type event handlers for type safety:
Form Events
Keyboard Events
Mouse Events
import type React from "react"
import { useState } from "react"
export default function TypedForm () {
const [ value , setValue ] = useState ( "" )
const handleSubmit = ( e : React . FormEvent < HTMLFormElement >) => {
e . preventDefault ()
console . log ( value )
}
const handleChange = ( e : React . ChangeEvent < HTMLInputElement >) => {
setValue ( e . target . value )
}
return (
< form onSubmit = { handleSubmit } >
< input value = { value } onChange = { handleChange } />
</ form >
)
}
Ref Types
Properly type component refs:
import { useRef , useEffect } from "react"
import type { ImperativePanelHandle } from "react-resizable-panels"
export default function TypedRefs () {
const textareaRef = useRef < HTMLTextAreaElement >( null )
const panelRef = useRef < ImperativePanelHandle >( null )
useEffect (() => {
// TypeScript knows these might be null
if ( textareaRef . current ) {
textareaRef . current . focus ()
}
if ( panelRef . current ) {
panelRef . current . resize ( 50 )
}
}, [])
return (
<>
< textarea ref = { textareaRef } />
</>
)
}
Type Assertions and Guards
Use type guards for runtime type safety:
function isFile ( item : FileTreeItem ) : item is FileItem {
return item . type === "file"
}
function isFolder ( item : FileTreeItem ) : item is FolderItem {
return item . type === "folder"
}
// Usage
const items : FileTreeItem [] = getFileTree ()
items . forEach (( item ) => {
if ( isFile ( item )) {
// TypeScript knows item is FileItem
console . log ( item . content )
} else if ( isFolder ( item )) {
// TypeScript knows item is FolderItem
console . log ( item . children )
}
})
Extending Component Types
Extend existing component types for custom props:
import type React from "react"
import { Button , type buttonVariants } from "@/components/ui/button"
import type { VariantProps } from "class-variance-authority"
interface IconButtonProps extends React . ComponentProps < "button" >,
VariantProps < typeof buttonVariants > {
icon : React . ReactNode
label : string
asChild ?: boolean
}
export function IconButton ({
icon ,
label ,
variant ,
size = "icon" ,
... props
} : IconButtonProps ) {
return (
< Button variant = { variant } size = { size } { ... props } >
{ icon }
< span className = "sr-only" > { label } </ span >
</ Button >
)
}
Real-World Example
Here’s a complete typed component from the Blocks codebase:
"use client"
import type React from "react"
import { useRef , useState } from "react"
import { Button } from "@/components/ui/button"
import { Textarea } from "@/components/ui/textarea"
import { cn } from "@/lib/utils"
interface ComposerState {
message : string
isExpanded : boolean
}
export default function TypedComposer () {
const [ state , setState ] = useState < ComposerState >({
message: "" ,
isExpanded: false ,
})
const textareaRef = useRef < HTMLTextAreaElement >( null )
const handleSubmit = ( e : React . FormEvent < HTMLFormElement >) => {
e . preventDefault ()
if ( state . message . trim ()) {
setState ({ message: "" , isExpanded: false })
if ( textareaRef . current ) {
textareaRef . current . style . height = "auto"
}
}
}
const handleChange = ( e : React . ChangeEvent < HTMLTextAreaElement >) => {
const newMessage = e . target . value
setState ({
message: newMessage ,
isExpanded: newMessage . length > 100 || newMessage . includes ( " \n " ),
})
if ( textareaRef . current ) {
textareaRef . current . style . height = "auto"
textareaRef . current . style . height = ` ${ textareaRef . current . scrollHeight } px`
}
}
const handleKeyDown = ( e : React . KeyboardEvent < HTMLTextAreaElement >) => {
if ( e . key === "Enter" && ! e . shiftKey ) {
e . preventDefault ()
handleSubmit ( e as unknown as React . FormEvent < HTMLFormElement >)
}
}
return (
< form onSubmit = { handleSubmit } >
< div
className = { cn (
"w-full max-w-2xl mx-auto border" ,
state . isExpanded ? "rounded-3xl" : "rounded-3xl"
) }
>
< Textarea
ref = { textareaRef }
value = { state . message }
onChange = { handleChange }
onKeyDown = { handleKeyDown }
placeholder = "Type a message"
className = "resize-none border-0"
/>
< Button type = "submit" disabled = { ! state . message . trim () } >
Send
</ Button >
</ div >
</ form >
)
}
Enable strict mode in tsconfig.json for maximum type safety.
TypeScript Configuration
The recommended tsconfig.json for Blocks:
{
"compilerOptions" : {
"target" : "ES2020" ,
"lib" : [ "ES2020" , "DOM" , "DOM.Iterable" ],
"jsx" : "preserve" ,
"module" : "ESNext" ,
"moduleResolution" : "bundler" ,
"strict" : true ,
"esModuleInterop" : true ,
"skipLibCheck" : true ,
"forceConsistentCasingInFileNames" : true ,
"resolveJsonModule" : true ,
"isolatedModules" : true ,
"incremental" : true ,
"paths" : {
"@/*" : [ "./*" ]
}
}
}
Best Practices
Use strict mode : Enable all strict type checking options
Avoid any : Use unknown or proper types instead
Export types : Export interfaces and types for component consumers
Type event handlers : Always type React event handlers explicitly
Use type inference : Let TypeScript infer types when possible
Document complex types : Add JSDoc comments for complex type definitions
Next Steps