npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@s3rd/canvas-artifacts

v0.1.6

Published

Drop-in Canvas editor for React AI applications

Downloads

57

Readme

@s3rd/canvas-artifacts

A drop-in React Canvas editor for AI applications. Give your chat UI a document editing experience — streaming-aware, fully controlled, no AI SDK lock-in.

Inspired by OpenCanvas (LangChain) and ChatGPT Canvas, distributed as a reusable npm package.


Getting Started

1. Install

npm install @s3rd/canvas-artifacts

Install peer dependencies if you don't already have them:

npm install react react-dom tailwindcss

2. Import styles

Add the stylesheet once — at your app entry point or global CSS file:

import '@s3rd/canvas-artifacts/styles.css'

3. Configure Tailwind

Add the package to your tailwind.config content paths so its classes are included:

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{ts,tsx}',
    './node_modules/@s3rd/canvas-artifacts/dist/**/*.{js,mjs}',
  ],
}

4. Render the Canvas

import { Canvas } from '@s3rd/canvas-artifacts'

function App() {
  const [content, setContent] = useState('# Hello\n\nStart typing...')

  return <Canvas value={content} onChange={setContent} />
}

value is a markdown string. onChange fires on every user edit. That's the whole API.


Demo

The demo-nextjs directory is a full working example built with Next.js and the Vercel AI SDK. It shows how to wire up a chat interface with a live canvas panel — the AI writes documents directly into the editor as it streams.

What it covers:

  • Server-side tool definition with canvasTool from @s3rd/canvas-artifacts/vercel-ai/server
  • Client-side canvas state via useCanvasTool from @s3rd/canvas-artifacts/vercel-ai
  • Streaming document content into the editor
  • Canvas action handling (polish, adjust length, suggest edits)

Run it locally:

cd demo-nextjs
cp .env.example .env.local   # add your OPENAI_API_KEY
npm install
npm run dev

Open http://localhost:3000 to see it in action.


Variants

inline (default)

Always renders an interactive editor in page flow.

<Canvas value={content} onChange={setContent} variant="inline" />

sideview

Shows a non-interactive preview inline. Clicking Edit slides open a side panel with the full editor.

<Canvas value={content} onChange={setContent} variant="sideview" />

both

Starts as an inline editor. An expand button in the toolbar transitions it to a side panel.

<Canvas value={content} onChange={setContent} variant="both" />

AI streaming

The canvas handles streaming out of the box. Just update value rapidly from your AI stream — the editor locks, appends content efficiently, and unlocks when the stream settles.

onChange is not called during streaming. It only fires on user-initiated edits.

// Works with any AI SDK — Vercel AI SDK, raw fetch, anything
const [content, setContent] = useState('')

// Stream into value — canvas handles the rest
useEffect(() => {
  streamFromAI((chunk) => setContent(prev => prev + chunk))
}, [])

<Canvas value={content} onChange={setContent} variant="sideview" />

Panel control

Use useCanvas to control the side panel programmatically — for example, auto-opening it when an AI response finishes.

import { Canvas, useCanvas } from '@s3rd/canvas-artifacts'

function App() {
  const [content, setContent] = useState('')
  const canvas = useCanvas({ defaultOpen: false })

  useEffect(() => {
    if (streamComplete) canvas.openPanel()
  }, [streamComplete])

  return (
    <Canvas
      {...canvas}
      value={content}
      onChange={setContent}
      variant="sideview"
    />
  )
}

useCanvas returns { open, onOpenChange, openPanel, closePanel, togglePanel }.


Props

| Prop | Type | Default | Description | |---|---|---|---| | value | string | — | Markdown string (required) | | onChange | (value: string) => void | — | Called on user edits (required) | | variant | 'inline' \| 'sideview' \| 'both' | 'inline' | Layout mode | | readOnly | boolean | false | Disables editing, shows static preview | | toolbar | boolean \| ToolbarConfig | true | Show/hide or configure toolbar | | open | boolean | — | Controlled panel open state | | defaultOpen | boolean | false | Initial panel open state | | onOpenChange | (open: boolean) => void | — | Panel open state callback | | className | string | — | Tailwind classes on the root element |

ToolbarConfig

All buttons are enabled by default. Pass { bold: false } to hide specific buttons.

interface ToolbarConfig {
  bold?: boolean
  italic?: boolean
  underline?: boolean
  strike?: boolean
  heading?: boolean      // h1/h2/h3
  bulletList?: boolean
  orderedList?: boolean
  blockquote?: boolean
  code?: boolean         // inline code
  link?: boolean
}

Theming

Tier 1 — CSS variables (no ejection needed):

:root {
  --canvas-bg: #ffffff;
  --canvas-border: #e2e8f0;
  --canvas-toolbar-bg: #f8fafc;
  --canvas-prose-color: #1a202c;
  --canvas-accent: #6366f1;
}

Dark mode is supported via the .dark class (shadcn/ui compatible).

Tier 2 — className prop:

<Canvas className="rounded-xl shadow-lg" value={content} onChange={setContent} />

Tier 3 — Eject and own the source (shadcn-style):

npx @s3rd/canvas-artifacts add toolbar
npx @s3rd/canvas-artifacts add canvas-preview
npx @s3rd/canvas-artifacts add canvas-layout

Copies the component into src/components/canvas/ — you own it from that point. The core/ layer (editor sync, markdown bridge, state machine) stays in node_modules and receives updates automatically.


License

MIT