Skip to content

Load only what a page uses

The SDK is one bundle, pixonaut.esm.js, and it is also three parts, each a file of its own on the CDN beside it. A page that reads its data somewhere else loads only the part it still needs.

File Holds Runs in
pixonaut-client.js A method for every read a key may make. No WebAssembly, no DOM. Anywhere fetch runs
pixonaut-core.js The designer: renderer, state, commands, events, checks, approve and commit. It takes its data as arguments. The browser
pixonaut-ui.js The generated form, its steps and the pickers, drawn over the core. The browser
pixonaut.esm.js All three, with Pixonaut.mount(). The browser

Every file is at https://cdn.pixonaut.app/sdk/<version>/, with its integrity on the releases page. Preload each file you import from there with its integrity, as Loading the SDK shows. The SDK is not published to npm: the CDN is where each part is loaded from. The client is MIT-licensed, so you may keep a copy of it in your own code and run it anywhere; the core and the UI may be bundled into your own site, and no further (see the licence). The renderer’s WebAssembly loads only with the core, and only when a designer is created.

The client has a method for every GET operation in the API reference, named from its operation id: templates.get is client.templates.get(id), templates.constraints.get is client.templates.constraints.get(id, { blankId, placementId }).

import { createClient } from 'https://cdn.pixonaut.app/sdk/<version>/pixonaut-client.js';
const client = createClient({ key: 'pk_…', baseUrl: 'https://api.pixonaut.app' });
const template = await client.templates.get('classic-tshirt@2');

What a method resolves is the API’s answer as plain JSON, so it can be serialised into a page and parsed there unchanged. A picture or a PDF resolves as the Response it came in.

In a browser, the client refuses a secret, integration or MCP key before it sends anything: those act as your whole account. Use a publishable key in a page.

A publishable key reads a shopper’s uploaded picture only in the designer session it was uploaded in. To read one with the client, pass that session as session, the x-pixonaut-session the designer sent.

A storefront that shows its templates as a gallery reads them all in one request, by the ids its own server hands the page:

const { items } = await client.templates.list({
ids: ['party-sign@3', 'party-sign-stars@1', 'cap-classic@2'],
});
// each item: { id, name, ratio, ratioTolerance, fieldKeys, previewUrl, … }

Each item names the template’s id, its name, the ratio a print size of it is judged against, the field keys its settings carry, and a previewUrl an <img> can load. Only live templates are answered, in the order you named them; a paused one, or an id the tenant has no published version of, is left out. To draw each one with what the customer entered, see A gallery of templates.

Which templates your storefront offers is yours to decide, so a publishable key reads no list of them: client.templates.list() without ids is refused with secret_key_required. Your server lists them with a secret key.

A refusal rejects with a PixonautApiError: its status is the HTTP status, its code is the API’s error code, and its requestId names the request when the answer carries one, to quote when you contact support.

createDesigner opens the template it is handed. It asks the API for no template and no placement, only for what a design itself needs: its uploads, its proof and its commit.

import { createDesigner } from 'https://cdn.pixonaut.app/sdk/<version>/pixonaut-core.js';
import { designerUi } from 'https://cdn.pixonaut.app/sdk/<version>/pixonaut-ui.js';
const designer = await createDesigner('#designer', {
publishableKey: 'pk_…',
apiBase: 'https://api.pixonaut.app',
template, // GET /v1/templates/{id}
placement, // GET /v1/templates/{id}/constraints?blankId=…&placementId=…
values, // getState().values, to restore a design in progress
ui: designerUi,
});
Option What it is
template The template, as GET /v1/templates/{id} answers it.
placement The template’s constraints at the blank and placement the design is for, as GET /v1/templates/{id}/constraints answers it with both named. Omitted, the design is the template’s own shape.
values The values the design starts from.
ui The UI’s designerUi. Omitted, the designer draws its canvas alone, for your own form: see Build your own designer.

Every other option is mount()’s, and the designer is the same object mount() resolves, with the same methods and events.

To keep even the publishable key out of the page, pass a transport in its place. The designer then sends every request through it: the proof, the commit, an upload and its file, and its asset and font reads. Your server sends each one on to Pixonaut with your key, and answers what Pixonaut answered.

const designer = await createDesigner('#designer', {
template,
transport: (path, init) => fetch(`/pixonaut${path}`, init),
ui: designerUi,
});

path is the API path, such as /v1/proofs. Keep the request’s method, body and headers, x-pixonaut-session among them, and add the authorization header on your server. Without x-pixonaut-session, a shopper’s own uploads do not show in the designer when your server sends them on with a publishable key: such a key reads an upload only in the session it was made in.

The server reads the template with the REST API and writes it into the page; the browser needs only the core and the UI. No SDK runs on the server.

<?php
function pixonaut_get(string $path): array {
$ch = curl_init('https://api.pixonaut.app' . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('PIXONAUT_SECRET_KEY')],
]);
$body = curl_exec($ch);
curl_close($ch);
return json_decode($body, true);
}
$id = 'classic-tshirt@2';
$data = [
'template' => pixonaut_get('/v1/templates/' . rawurlencode($id)),
'placement' => pixonaut_get('/v1/templates/' . rawurlencode($id)
. '/constraints?' . http_build_query(['blankId' => 'tee', 'placementId' => 'front'])),
];
?>
<div id="designer"></div>
<script type="application/json" id="pixonaut-data">
<?= json_encode($data, JSON_HEX_TAG | JSON_HEX_AMP) ?>
</script>
<link rel="modulepreload" href="https://cdn.pixonaut.app/sdk/<version>/pixonaut-core.js" integrity="sha384-…" crossorigin="anonymous">
<link rel="modulepreload" href="https://cdn.pixonaut.app/sdk/<version>/pixonaut-ui.js" integrity="sha384-…" crossorigin="anonymous">
<script type="module">
import { createDesigner } from 'https://cdn.pixonaut.app/sdk/<version>/pixonaut-core.js';
import { designerUi } from 'https://cdn.pixonaut.app/sdk/<version>/pixonaut-ui.js';
const { template, placement } = JSON.parse(
document.getElementById('pixonaut-data').textContent,
);
await createDesigner('#designer', {
publishableKey: 'pk_…',
apiBase: 'https://api.pixonaut.app',
template,
placement,
ui: designerUi,
});
</script>

A server component reads the template with the client and passes it to a client component as props; the client component loads the core and the UI. A server cannot import from a URL, so keep a copy of the client, and its declarations, in the app first:

Terminal window
curl -o lib/pixonaut-client.js https://cdn.pixonaut.app/sdk/<version>/pixonaut-client.js
curl -o lib/pixonaut-client.d.ts https://cdn.pixonaut.app/sdk/<version>/pixonaut-client.d.ts

Check the copy against the file’s integrity on the releases page, and fetch it again when you move to a new release.

app/design/[id]/page.tsx
import { createClient } from '@/lib/pixonaut-client.js';
import { Designer } from './designer';
const pixonaut = createClient({
key: process.env.PIXONAUT_SECRET_KEY!,
baseUrl: 'https://api.pixonaut.app',
});
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
return <Designer template={await pixonaut.templates.get(id)} />;
}
app/design/[id]/designer.tsx
'use client';
import { useEffect, useRef } from 'react';
const SDK = 'https://cdn.pixonaut.app/sdk/<version>';
export function Designer({ template }: { template: object }) {
const el = useRef<HTMLDivElement>(null);
useEffect(() => {
let designer: { destroy(): void } | undefined;
let gone = false;
(async () => {
const { createDesigner } = await import(/* webpackIgnore: true */ `${SDK}/pixonaut-core.js`);
const { designerUi } = await import(/* webpackIgnore: true */ `${SDK}/pixonaut-ui.js`);
const made = await createDesigner(el.current, {
publishableKey: process.env.NEXT_PUBLIC_PIXONAUT_KEY,
apiBase: 'https://api.pixonaut.app',
template,
ui: designerUi,
});
if (gone) made.destroy();
else designer = made;
})();
return () => {
gone = true;
designer?.destroy();
};
}, [template]);
return <div ref={el} />;
}

Preload both files with their integrity in the page’s <head>. Each file’s declarations are served beside it (pixonaut-core.d.ts, pixonaut-ui.d.ts). The declarations and the client are MIT-licensed; the core and the UI are not.

An app that reads everything in the browser loads the whole SDK and mounts by template id, as Embed the designer describes:

import { useEffect, useRef } from 'react';
export function Designer({ templateId }: { templateId: string }) {
const el = useRef<HTMLDivElement>(null);
useEffect(() => {
let designer: { destroy(): void } | undefined;
let gone = false;
(async () => {
const { mount } = await import(
/* webpackIgnore: true */ 'https://cdn.pixonaut.app/sdk/<version>/pixonaut.esm.js'
);
const made = await mount(el.current, {
publishableKey: 'pk_…',
apiBase: 'https://api.pixonaut.app',
templateId,
});
if (gone) made.destroy();
else designer = made;
})();
return () => {
gone = true;
designer?.destroy();
};
}, [templateId]);
return <div ref={el} />;
}

The whole SDK holds the client too, as createClient, for the app’s own reads with its publishable key.

Questions? Write to [email protected].

Status · Terms of service · Privacy policy · Subprocessors