
Security News
Node.js Drops Bug Bounty Rewards After Funding Dries Up
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.
The Shadcn for design and marketing. A template-based CLI tool for generating images and videos using React + Tailwind CSS + Satori.
text-primary, bg-card, etc.)bg-primary/50)npx dsgn add template-namenpm install -g dsgn
Or use with npx:
npx dsgn --help
Templates can be installed from multiple sources:
dsgn add banner-hero
dsgn add og-image --registry https://custom-registry.com/r
# From a GitHub repo (looks for template.json in repo root)
dsgn add github:username/repo
# From a specific path in the repo
dsgn add github:username/repo/templates/banner-hero
# From an organization
dsgn add github:myorg/design-templates/social-media/og-image
How it works:
template.json from the specified pathmain branch by default# Install from any publicly accessible URL
dsgn add https://example.com/templates/my-template.json
dsgn add https://cdn.example.com/templates/awesome-banner.json
Requirements:
# Relative path
dsgn add ./my-templates/banner-hero
dsgn add ../shared-templates/product-card
# Absolute path
dsgn add /Users/you/templates/social-card
Use cases:
Templates are installed to: _dsgn/templates/<template>/ (customizable in dsgn.json)
Benefits:
See REGISTRY_SETUP.md to create your own registry.
dsgn render banner-hero \
--props '{"title":"Hello World","subtitle":"Welcome to dsgn"}'
By default, images are saved to dsgn/outputs/ in your current project.
You can specify a custom output path:
dsgn render banner-hero \
--props '{"title":"Hello World","subtitle":"Welcome to dsgn"}' \
--out custom/path/banner.png
Or use a props file:
# props.json
{
"title": "Hello World",
"subtitle": "Welcome to dsgn"
}
dsgn render banner-hero --props props.json --out banner.png
Output formats:
# PNG (default) - best for photos and complex gradients
dsgn render banner-hero --props props.json --format png -o banner.png
# SVG - scalable vector, smaller file size, perfect for template composition
dsgn render banner-hero --props props.json --format svg -o banner.svg
# WebP - modern format, smaller than PNG with similar quality
dsgn render banner-hero --props props.json --format webp -o banner.webp
# JPEG - smallest file size, good for photos
dsgn render banner-hero --props props.json --format jpg -o banner.jpg
SVG benefits with template composition:
dsgn list
dsgn validate
dsgn validate banner-hero
Use dsgn programmatically in your Next.js API routes, serverless functions, or Node.js applications!
npm install dsgn
import { defineTemplate, renderImage } from 'dsgn/sdk';
// Define template programmatically
const ogTemplate = defineTemplate({
name: 'og-image',
size: { width: 1200, height: 630 },
render: ({ tw, title, description }) => (
<div style={tw('flex flex-col w-full h-full bg-gradient-to-br from-blue-600 to-purple-700 p-12')}>
<h1 style={tw('text-6xl font-bold text-white')}>{title}</h1>
<p style={tw('text-2xl text-white/80')}>{description}</p>
</div>
),
});
// Render to buffer
const png = await renderImage(ogTemplate, {
title: 'Hello World',
description: 'Generated with dsgn SDK',
});
// Use in Next.js API route
export default async function handler(req, res) {
const png = await renderImage(ogTemplate, req.query);
res.setHeader('Content-Type', 'image/png');
res.send(png);
}
import { defineTemplate, renderVideo } from 'dsgn/sdk';
const introTemplate = defineTemplate({
name: 'intro-video',
type: 'video',
size: { width: 1920, height: 1080 },
video: { fps: 30, duration: 3 },
render: ({ tw, progress, title }) => {
const opacity = progress; // 0 to 1 animation
return (
<div style={tw('flex items-center justify-center w-full h-full bg-black')}>
<h1 style={{ ...tw('text-8xl font-bold text-white'), opacity }}>
{title}
</h1>
</div>
);
},
});
const mp4 = await renderVideo(introTemplate, { title: 'Welcome!' });
See the full example in examples/nextjs-api/
// pages/api/og-image.ts
import { defineTemplate, renderImage } from 'dsgn/sdk';
const template = defineTemplate({
name: 'og-image',
size: { width: 1200, height: 630 },
render: ({ tw, title }) => (
<div style={tw('flex items-center justify-center w-full h-full bg-white')}>
<h1 style={tw('text-6xl font-bold')}>{title}</h1>
</div>
),
});
export default async function handler(req, res) {
const png = await renderImage(template, req.query);
res.setHeader('Content-Type', 'image/png');
res.setHeader('Cache-Control', 's-maxage=86400');
res.send(png);
}
Usage:
https://yoursite.com/api/og-image?title=Hello+World
dsgn uses shadcn/ui's design system by default! All templates have access to semantic color tokens:
// Use shadcn colors in templates
<div style={tw('bg-card border rounded-xl p-16')}>
<h1 style={tw('text-foreground font-bold')}>Title</h1>
<p style={tw('text-muted-foreground')}>Subtitle</p>
</div>
Supported shadcn classes:
text-primary, bg-secondary, text-muted-foreground, bg-card, border, bg-destructivebg-primary/50, text-muted-foreground/75, border/30dsgn.jsonSee SHADCN_INTEGRATION.md for complete documentation.
Templates automatically use your project's design tokens defined in dsgn.json. This allows generated images to match your brand colors and design system.
dsgn init
This creates a dsgn.json file in your project:
{
"colors": {
"primary": "#667eea",
"secondary": "#764ba2",
"background": "#ffffff",
"foreground": "#000000",
"muted": "#f3f4f6",
"accent": "#3b82f6",
"destructive": "#ef4444",
"border": "#e5e7eb"
},
"fonts": {
"sans": ["Inter", "system-ui", "sans-serif"],
"mono": ["JetBrains Mono", "monospace"]
},
"tokens": {
"borderRadius": {
"sm": "0.25rem",
"md": "0.5rem",
"lg": "1rem",
"xl": "1.5rem"
}
},
"defaults": {
"width": 1200,
"height": 630
}
}
Templates receive the config via props:
export default function MyTemplate({ title, config }) {
const primaryColor = config?.colors?.primary || '#000';
return (
<div style={{ background: primaryColor }}>
{title}
</div>
);
}
This ensures your generated images use the same colors as your Tailwind/Shadcn setup!
Templates can use Tailwind utility classes for styling! The tw() function is automatically provided:
export default function MyTemplate({ title, tw }) {
return (
<div style={tw('flex items-center justify-center w-full h-full bg-primary text-white')}>
<h1 style={tw('text-6xl font-bold')}>
{title}
</h1>
</div>
);
}
Combine with custom styles:
<div
style={{
...tw('flex flex-col p-20 text-white'),
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
}}
>
<h1 style={tw('text-8xl font-bold')}>{title}</h1>
</div>
dsgn automatically detects and uses your tailwind.config.js!
If you have a Tailwind project:
// tailwind.config.js
export default {
theme: {
extend: {
colors: {
primary: '#667eea',
brand: {
500: '#0ea5e9',
900: '#0c4a6e',
},
},
spacing: {
'72': '18rem',
},
},
},
};
Your templates can use these values:
<div style={tw('bg-primary text-white p-72')}>
<div style={tw('bg-brand-500')}>Custom brand color!</div>
</div>
Config priority:
tailwind.config.js (if exists)dsgn.json (fallback)Supported classes:
flex, flex-col, items-center, justify-between, etc.p-4, px-8, m-6, gap-4, etc.text-6xl, font-bold, text-center, etc.bg-primary, text-brand-500, etc. (from your config!)opacity-90, rounded-xl, etc.bg-card, text-foreground, text-muted-foregroundbg-primary/50, text-muted-foreground/75Note: Tailwind v4 uses CSS variables instead of JS config. We're working on CSS parsing support. For now, use dsgn.json or tailwind.config.js (v3 format).
Templates can use default fonts (from CDN) or bundle custom fonts for brand-specific typography.
By default, templates use Noto Sans fetched from jsDelivr CDN. No setup required!
Templates can bundle their own fonts (WOFF, WOFF2, TTF, OTF):
my-template/
├── my-template.tsx
├── meta.json
└── fonts/
├── CustomFont-Regular.woff
└── CustomFont-Bold.woff
meta.json:
{
"name": "my-template",
"fonts": [
{
"name": "Custom Font",
"path": "fonts/CustomFont-Bold.woff",
"weight": 700,
"style": "normal"
}
]
}
Template usage:
<h1 style={{ fontFamily: 'Custom Font', fontWeight: 700 }}>
Branded Typography
</h1>
Use cases:
Templates have access to built-in helper functions:
tw(classes) - Convert Tailwind classes to inline stylesqr(text, options?) - Generate QR codes from text/URLsimage(propKey) - Embed images as data URIsvideo(propKey) - Embed video frames (for video templates)template(name, props) - Embed other templatesconfig - Access dsgn.json configurationGenerate QR codes from any text or URL using the qr() helper:
export default function Template({ title, url, qr, tw }) {
return (
<div style={tw('flex flex-col items-center p-16')}>
<h1 style={tw('text-5xl font-bold mb-8')}>{title}</h1>
{/* QR code auto-generated from url prop */}
<img src={qr(url)} width={200} height={200} alt="QR Code" />
<p style={tw('text-xl mt-4')}>{url}</p>
</div>
);
}
Usage:
dsgn render qr-card '{
"title": "Visit Our Website",
"url": "https://dsgncli.com"
}'
Customization:
// Custom size and error correction
<img src={qr(url, {
width: 300,
margin: 2,
errorCorrectionLevel: 'H', // L, M, Q, H (higher = more error correction)
color: {
dark: '#000000',
light: '#FFFFFF'
}
})} />
How it works:
qr() with any prop value (url, text, etc.)<img src={...} />Use cases:
Embed images (jpg, png, gif, webp, svg) as data URIs using the image() helper:
export default function Banner({ background, tw, image }) {
return (
<div style={tw('relative w-full h-full')}>
<img src={image('background')} style={tw('absolute inset-0 w-full h-full object-cover')} />
<h1 style={tw('relative text-6xl font-bold text-white')}>Hello World</h1>
</div>
);
}
Props format:
{
"background": "./path/to/image.jpg"
}
Usage:
dsgn render banner '{
"background": "./my-background.jpg"
}'
How it works:
image('propKey') to get the data URI for that propUse cases:
For video templates, embed video frames synchronized to the current animation frame using the video() helper:
export default function VideoOverlay({ background, title, tw, video, frame, progress }) {
const opacity = progress < 0.2 ? progress / 0.2 : 1;
return (
<div style={tw('relative w-full h-full')}>
{/* Background video - auto-syncs to current frame */}
<img
src={video('background')}
style={tw('absolute inset-0 w-full h-full object-cover')}
/>
{/* Animated overlay */}
<h1
style={{
...tw('relative text-8xl font-bold text-white'),
opacity
}}
>
{title}
</h1>
</div>
);
}
Props format:
{
"title": "My Video",
"background": "./my-video.mp4"
}
Usage:
dsgn render video-overlay props.json --out output.mp4
How it works:
Use cases:
Embed one template inside another using the template() helper. This is perfect for creating composite designs where smaller templates are reused as building blocks.
Just call template() - no declaration needed:
export default function Template({ title, bannerTitle, bannerSubtitle, template, tw }) {
return (
<div style={tw('w-full h-full flex flex-col bg-slate-900 p-12')}>
<div style={tw('bg-white rounded-3xl p-10 flex flex-col')}>
<h1 style={tw('text-5xl font-bold mb-8')}>{title}</h1>
{/* Embed any template - auto-loaded on first render */}
<div style={tw('rounded-xl overflow-hidden flex')}>
{template('image', { title: bannerTitle, subtitle: bannerSubtitle })}
</div>
</div>
</div>
);
}
Usage:
dsgn render composite-card '{
"title": "Product Launch",
"bannerTitle": "New Release",
"bannerSubtitle": "Coming Soon"
}'
How it works:
template('any-template-name')meta.json for sizingPass props directly:
{template('image', { title: 'Custom Title', subtitle: 'Custom Subtitle' })}
{template('qr-card', { url: 'https://example.com', title: 'Scan Me' })}
Use cases:
Templates are TSX files with metadata. Each template can be either an image or a video template (not both).
export const meta = {
name: "banner-hero",
description: "Hero banner with shadcn design",
type: "image", // Optional, defaults to "image"
size: { width: 1600, height: 900 },
props: {
title: "string",
subtitle: "string?"
}
};
export default function Template({ title, subtitle, tw, qr, template }) {
return (
<div style={tw('w-full h-full flex flex-col justify-center p-20 bg-background')}>
<div style={tw('bg-card border rounded-xl p-16 flex flex-col')}>
<h1 style={tw('text-8xl font-bold text-foreground')}>{title}</h1>
{subtitle && (
<p style={tw('text-4xl text-muted-foreground/75')}>{subtitle}</p>
)}
</div>
</div>
);
}
export const meta = {
name: "animated-banner",
description: "Animated hero banner with shadcn design",
type: "video",
size: { width: 1600, height: 900 },
props: {
title: "string",
subtitle: "string?"
},
video: {
fps: 30,
duration: 3
}
};
export default function Template({ title, subtitle, frame, progress, tw, qr, template }) {
const opacity = Math.min(1, progress * 2);
const translateY = 20 - (progress * 20);
return (
<div style={tw('w-full h-full flex flex-col justify-center items-center bg-background p-20')}>
<div style={tw('bg-card border rounded-xl p-16 flex flex-col')}>
<h1
style={{
...tw('text-9xl font-bold text-foreground'),
opacity,
transform: `translateY(${translateY}px)`
}}
>
{title}
</h1>
{subtitle && (
<p style={tw('text-5xl text-muted-foreground/75 mt-6')}>
{subtitle}
</p>
)}
</div>
</div>
);
}
dsgn initInitialize a dsgn.json config file with default design tokens.
dsgn add <template>Install a template from the registry.
Options:
-r, --registry <url> - Custom registry URLdsgn listList all installed templates with metadata.
dsgn render <template>Render a template to an image.
Options:
-p, --props <file> - Props file (JSON)-o, --out <file> - Output file path--format <format> - Output format: png, svg, webp (default: png)--video - Render as video (coming soon)dsgn validate [template]Validate template metadata. If no template is specified, validates all installed templates.
dsgn automatically validates templates and props before rendering, providing helpful error messages.
# Missing required props
$ dsgn render banner-hero --props '{}'
✖ Template validation failed
✗ props: Missing required prop: "title"
→ Add "title" to your props: --props '{"title":"value"}'
# Wrong prop type
$ dsgn render banner-hero --props '{"title":123}'
✖ Template validation failed
✗ props: Prop "title" should be a string, got number
→ Change "title" to a string: "title": "value"
When rendering fails, dsgn provides clear suggestions:
$ dsgn render my-template --props props.json
✖ Failed to render template
Error: Satori requires explicit display: flex for containers with multiple children
Suggestions:
• Add "flex" or "flex-col" to your Tailwind classes: tw("flex items-center")
• Or add display: flex directly: style={{ display: "flex", ...tw("...") }}
• Every <div> with 2+ children MUST have display: flex or display: none
See VALIDATION.md for complete validation documentation and troubleshooting.
Templates are stored in your project's dsgn/templates/ directory. You can create custom templates:
mkdir -p dsgn/templates/my-template
meta.json:{
"name": "my-template",
"description": "My custom template",
"type": "image",
"size": { "width": 1200, "height": 630 },
"props": {
"title": "string"
}
}
my-template.tsx:export default function MyTemplate({ title }) {
return (
<div style={{
display: 'flex',
width: '100%',
height: '100%',
background: '#000',
color: '#fff',
justifyContent: 'center',
alignItems: 'center',
fontSize: '48px',
fontFamily: 'sans-serif',
}}>
{title}
</div>
);
}
dsgn validate my-template
dsgn render my-template --props '{"title":"Hello"}' --out test.png
Video templates use type: "video" and support frame and progress props for animations.
dsgn uses pure JavaScript/WASM for video encoding - works everywhere!
✅ Works on Vercel Edge Functions ✅ Works on Netlify Functions ✅ Works on Cloudflare Workers ✅ Works on AWS Lambda ✅ Works everywhere (Docker, CI/CD, local)
12x faster than traditional approaches - renders a 3-second video in ~2 seconds!
Video templates are similar to image templates but include frame and progress props for animations:
export const meta = {
name: "my-video",
type: "video",
size: { width: 1200, height: 630 },
props: { title: "string" },
video: {
fps: 30,
duration: 3
}
};
export default function Template({ title, frame, progress, tw, qr, template }) {
return (
<div
style={{
...tw('w-full h-full flex items-center justify-center'),
opacity: progress,
transform: `translateY(${20 - progress * 20}px)`
}}
>
<h1 style={tw('text-6xl font-bold')}>{title}</h1>
</div>
);
}
# Render to MP4
dsgn render my-video '{"title":"Animated Title"}' -o output.mp4
# With custom quality settings
dsgn render my-video props.json --crf 18 # Higher quality (lower CRF = better quality)
# Export frames only (for manual encoding)
dsgn render my-video props.json --frames-only -o frames/
How it works:
Props available in video templates:
frame - Current frame number (0 to totalFrames-1)progress - Animation progress from 0 to 1tw, qr, template, configPerformance:
A full documentation website is available in the website/ folder, built with Astro.
Local development:
cd website
npm install
npm run dev
Deployment:
The site is ready to deploy on Netlify, Vercel, or Cloudflare Pages. A netlify.toml is included for easy Netlify deployment.
See website/README.md for more details.
Templates are fetched from a registry (similar to shadcn/ui). The default registry is:
https://dsgncli.com/r
You can host your own registry and use it with:
dsgn add my-template --registry https://my-registry.com
MIT
FAQs
A CLI tool for AI code agents and developers for generating images and videos.
We found that dsgn demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.