Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/andresilva-cc/grafex/llms.txt

Use this file to discover all available pages before exploring further.

A variant is a named override set that lets one composition file produce multiple distinct outputs. Instead of maintaining separate files for your Open Graph image, Twitter card, and square thumbnail, you define a variants map in config and let Grafex render them all in a single pass. Each variant inherits every field from the base config and can replace any of them — dimensions, format, props, CSS, fonts, or HTML attributes.

Defining variants

Add a variants key to your CompositionConfig. Each entry is a variant name mapped to a VariantConfig object. A VariantConfig accepts the same fields as CompositionConfig minus the variants key itself.
// card.tsx
import type { CompositionConfig } from 'grafex';

export const config: CompositionConfig = {
  width: 1200,
  height: 630,
  format: 'png',
  variants: {
    og: {},
    twitter: { width: 1200, height: 675 },
    square: { width: 1080, height: 1080, props: { layout: 'square' } },
  },
};

interface Props {
  layout?: string;
}

export default function Card({ layout = 'default' }: Props) {
  return (
    <div
      style={{
        width: '100%',
        height: '100%',
        backgroundColor: '#1e293b',
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        fontFamily: 'system-ui, sans-serif',
        color: 'white',
        fontSize: '48px',
      }}
    >
      {layout}
    </div>
  );
}
The og variant has an empty config object — it inherits the base 1200×630 dimensions exactly. The twitter variant overrides only the height. The square variant changes both dimensions and also passes a layout prop to the component so it can adjust its layout for the square aspect ratio.

Merge rules

Understanding how variant fields are merged with the base config prevents surprises:

Scalar fields

Fields like width, height, scale, format, and quality replace the base value when set in the variant. If not set in the variant, the base config value is used.

props

Shallow-merged: variant props are applied first, then CLI/API props override individual keys on top. Neither replaces the other wholesale.

Array fields (fonts, css)

Replaced entirely — the variant’s array is used as-is instead of the base config’s array. There is no concatenation.

htmlAttributes

Spread over base: variant attributes are shallow-merged on top of the base htmlAttributes object, so you can override one key without losing the others.
The full priority chain, from highest to lowest, is: CLI/API options → variant config → base config → defaults.

Exporting variants from the CLI

When a composition defines variants, grafex export renders all of them by default and names each output file after the variant key.
# Renders og.png, twitter.png, square.png in the current directory
grafex export -f card.tsx

# Same, but into a specific directory
grafex export -f card.tsx -o ./images/
When you pass -o with a directory path (trailing slash or an existing directory), Grafex writes <variant>.<format> files inside it. When you pass a file path, you must also pass --variant to select which variant to render.
To export a single named variant to a specific file:
grafex export -f card.tsx --variant og -o card-og.png

Rendering variants via the Node.js API

Use render to produce a single named variant:
import { render, close } from 'grafex';

const result = await render('./card.tsx', { variant: 'twitter' });
// result.buffer — the rendered image
// result.width  — effective pixel width (after scale)
// result.height — effective pixel height (after scale)
Use renderAll to render every variant in one call. It returns a Map<string, RenderResult> keyed by variant name:
import { renderAll, close } from 'grafex';
import { writeFileSync } from 'node:fs';

const all = await renderAll('./card.tsx', { props: { title: 'Hello' } });

for (const [name, result] of all) {
  writeFileSync(`${name}.${result.format}`, result.buffer);
}

await close();
renderAll accepts the same options as render except for variant. Options you pass (such as props, scale, or format) are applied on top of each variant’s resolved config.

Variants with htmlAttributes

Variants can override individual HTML attributes without replacing the whole set. In the example below, both variants inherit lang: 'en' from the base config, but each sets a different data-theme:
import type { CompositionConfig } from 'grafex';

export const config: CompositionConfig = {
  width: 800,
  height: 400,
  htmlAttributes: {
    lang: 'en',
    'data-theme': 'light',
  },
  variants: {
    default: {},
    dark: {
      htmlAttributes: {
        'data-theme': 'dark',
      },
    },
  },
};

export default function WithHtmlAttributesVariant() {
  return (
    <div
      style={{
        width: '800px',
        height: '400px',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontFamily: 'system-ui, sans-serif',
      }}
    >
      Variant HTML Attributes
    </div>
  );
}
The default variant renders with <html lang="en" data-theme="light">. The dark variant renders with <html lang="en" data-theme="dark">lang is inherited, only data-theme changes.
Array fields (css and fonts) are replaced, not merged. If your base config sets css: ['./base.css'] and a variant sets css: ['./dark.css'], the variant uses only dark.css. Include all required stylesheets in each variant’s array if needed.

Build docs developers (and LLMs) love