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
Section titled “The client”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.
The templates of a gallery
Section titled “The templates of a gallery”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.
The core
Section titled “The core”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.
Sending its requests through your server
Section titled “Sending its requests through your server”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.
A PHP page
Section titled “A PHP page”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.
<?phpfunction 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 Next.js app
Section titled “A Next.js app”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:
curl -o lib/pixonaut-client.js https://cdn.pixonaut.app/sdk/<version>/pixonaut-client.jscurl -o lib/pixonaut-client.d.ts https://cdn.pixonaut.app/sdk/<version>/pixonaut-client.d.tsCheck the copy against the file’s integrity on the releases page, and fetch it again when you move to a new release.
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)} />;}'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.
A single-page React app
Section titled “A single-page React app”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].