Templates library

You can use Polotno tools and ability to customize side panel to make your own library of templates.

In order to make a templates library you will need:

  1. Create a design in the Polotno Editor

  2. Use store.toJSON() to save the design state on computer as .json file

  3. (Optionally) Use store.saveAsImage() to save preview file of a design

  4. Repeat 1-4 as many times as you need in order to make a library of templates

  5. If you skipped step number 3 you can use polotno-node to generate previews automatically

  6. Make server side API to access list of templates

  7. Create a custom side panel to display list of templates from that API

  8. Use store.loadJSON()


(1-4) Making designs library

To make Polotno-friendly designs a Polotno-based editor must be used.

Feel free to use https://studio.polotno.com/ if you want; when a design is ready, hit "save' button to download .json file.


(5) Generating previews

The polotno-node API can be used to efficiently generate small previews of the entire library. A NodeJS script can be written to read a directory and create previews for every JSON file contained in it. Upon completion of the script, a folder will be generated that includes the designs (JSON files) and their previews (PNG files).

If you prefer not to use NodeJS, you can use Polotno Cloud API to generate previews with your own backend language.

import fs from 'fs';
import path from 'path';
import { createInstance } from 'polotno-node';

// we will look for local directory for templates
const FOLDER_NAME = 'templates';

async function run() {
  // create working instance
  const instance = await createInstance({
    // this is a demo key just for that project
    // (!) please don't use it in your projects
    // to create your own API key please go here: https://polotno.com/cabinet
    key: 'nFA5H9elEytDyPyvKL7T',
  });

  // read all files in the directory
  const files = fs.readdirSync(folder);
  for (const file of files) {
    // if it is not a json file - skip it
    if (file.indexOf('.json') === -1) {
      continue;
    }

    // load file
    const data = fs.readFileSync(path.join(folder, file)).toString();
    const json = JSON.parse(data);

    // convert JSON into image preview
    const imageBase64 = await instance.run(async (json) => {
      store.loadJSON(json);
      await store.waitLoading();
      // set width of previews
      // we usually don't need original size there
      const maxWidth = 200;
      const scale = maxWidth / store.width;
      const url = await store.toDataURL({ pixelRatio: scale });
      return url.split('base64,')[1];
    }, json);

    // save images locally into the same folder
    fs.writeFileSync(
      path.join(folder, file.split('.')[0] + '.png'),
      imageBase64,
      'base64'
    );
    console.log(`Finished ${folder} ${file}`);
  }
  // close instance
  instance.close();
}

run();


(6) Making backend API to load templates

This step is out of Polotno scope. You can use any backend stack you like. Most likely you will need API to get a list of available templates. For every design you will need to share public path to json file and png preview.


(7 and 8) Displaying templates on side panel

As soon as you have backend API to get list of templates, you can use Side Panel Customization to display that list on the page. In the demo we will use <ImagesGrid /> component and useInfiniteAPI hook (Docs for them). You don't have to use them in your app. You can make your own implementation. But these modules may save you a lot of time, because they are designed for such use cases. Polotno uses them internally for default side panels.

The side panel may look like this:

import React from 'react';
import { observer } from 'mobx-react-lite';
import { useInfiniteAPI } from 'polotno/utils/use-api';

import { SectionTab } from 'polotno/side-panel';
import MdPhotoLibrary from '@meronex/icons/md/MdPhotoLibrary';

import { ImagesGrid } from 'polotno/side-panel/images-grid';

export const TemplatesPanel = observer(({ store }) => {
  // load data
  const { data, isLoading } = useInfiniteAPI({
    getAPI: ({ page }) => `templates/page${page}.json`,
  });

  return (
    <div style={{ height: '100%' }}>
      <ImagesGrid
        shadowEnabled={false}
        images={data?.map((data) => data.items).flat()}
        getPreview={(item) => `/templates/${item.preview}`}
        isLoading={isLoading}
        onSelect={async (item) => {
          // download selected json
          const req = await fetch(`/templates/${item.json}`);
          const json = await req.json();
          // just inject it into store
          store.loadJSON(json);
        }}
        rowsNumber={1}
      />
    </div>
  );
});

// define the new custom section
export const TemplatesSection = {
  name: 'custom-templates',
  Tab: (props) => (
    <SectionTab name="Custom templates" {...props}>
      <MdPhotoLibrary />
    </SectionTab>
  ),
  // we need observer to update component automatically on any store changes
  Panel: TemplatesPanel,
};

News, updates and promos – be the first to get 'em

News, updates and promos – be the first to get 'em