Recipes
Copy-paste starting points for common transforms. Client setup → Quickstart.
Convert a format
The one-liner. Great for shrinking whatever a browser handed you.
format.ts
const [r] = await tk
.runQueue([{ bytes, filename: 'photo.jpg' }], 'image')
.convert({ format: 'webp' });
if (r.ok) console.log(r.outputs[0]!.media.url);Resize with a dimension cap
maxSizecaps the longest side and preserves aspect ratio. It never upscales, so it's safe to apply to inputs of any size.
resize.ts
const [r] = await tk
.runQueue([{ bytes, filename: 'photo.png' }], 'image')
.maxSize(1024) // fits inside 1024×1024, keeps ratio, no upscale
.convert({ format: 'jpg', quality: 80 });
if (r.ok) console.log(r.outputs[0]!.media.url);Responsive image set (fan-out)
One upload, many sizes. Build a graph on Pipelines and hand it to runPipeline — the output suffix doubles as the width here.
srcset.ts
import { mergePipelineNodeConfig, type Pipeline } from '@transform-kit/sdk';
function srcsetPipeline(widths: number[]): Pipeline {
const nodes: Pipeline['nodes'] = [{ id: 'in', type: 'pipeline.input' }];
const edges: Pipeline['edges'] = [];
for (const w of widths) {
nodes.push(
{ id: `r${w}`, type: 'image.resize', config: mergePipelineNodeConfig('image.resize', { mode: 'pixels', width: w, height: w, fit: 'inside' }) },
{ id: `c${w}`, type: 'image.convert', config: mergePipelineNodeConfig('image.convert', { format: 'webp' }) },
{ id: `o${w}`, type: 'pipeline.output', config: mergePipelineNodeConfig('pipeline.output', { suffix: String(w) }) },
);
edges.push(
{ source: 'in', target: `r${w}` },
{ source: `r${w}`, target: `c${w}` },
{ source: `c${w}`, target: `o${w}` },
);
}
return { nodes, edges };
}
const [photo] = await tk.runPipeline(
[{ bytes, filename: 'photo.png' }],
srcsetPipeline([320, 640, 1024, 1600]),
);
// -> "https://… 320w, https://… 640w, …"
const srcset = photo.ok ? photo.outputs.map((o) => `${o.media.url} ${o.output}w`).join(', ') : '';Video: resize and re-encode
clip.ts
const results = await tk
.runQueue(files, 'video')
.maxSize(720)
.convert({ format: 'mp4', quality: 75 });
for (const r of results) {
if (r.ok) console.log(r.filename, r.outputs[0]!.media.url);
}Audio: convert
tone.ts
const results = await tk
.runQueue(files, 'audio')
.convert({ format: 'mp3', bitrate: 192 });
for (const r of results) {
if (r.ok) console.log(r.filename, r.outputs[0]!.media.url);
}PDF to markdown
brief.ts
import { readFile } from 'node:fs/promises';
const [result] = await tk
.runQueue([{ bytes: await readFile('brief.pdf'), filename: 'brief.pdf' }], 'document')
.convert({ format: 'md' });
if (result.ok) {
const markdown = await fetch(result.outputs[0]!.media.url).then((r) => r.text());
console.log(markdown);
}Catch the one thing that throws
A bad file comes back as ok: false (see Core concepts). A malformed pipeline is the exception: it throws a TransformKitError before anything uploads, so wrap the call if you build graphs dynamically.
errors.ts
import { TransformKit, TransformKitError } from '@transform-kit/sdk';
try {
const results = await tk
.runQueue(files, 'image')
.convert({ format: 'webp' })
.options({ onProgress: (e) => console.log(e.stage, e.filename ?? e.index) });
for (const r of results) {
if (r.ok) console.log(r.filename, r.outputs[0]!.media.url);
else console.error(r.filename, r.error); // e.g. "This request needs 1 credit(s)…"
}
} catch (err) {
// Thrown before upload — the pipeline itself was invalid.
if (err instanceof TransformKitError && err.code === 'invalid_pipeline') {
console.error(err.message);
}
throw err;
}Next steps
- Node.js — batch a folder with lazy loaders, and bring your own storage.
- Next.js — take an upload from the browser and transform it on your server.
- HTTP & curl — the raw calls behind these recipes.