May 12, 2024

ExportX Figma Plugin User Guide


Quick Start

Our plugin supports both “Design Mode” and “Dev Mode”. Check out the video below to quickly understand how to use the plugin.

Supported Export Formats and Compression Rates

We support compressing images in multiple formats and at multiple rates simultaneously, but the rules are limited to 5. The supported image formats are:

FormatSupportedSpace Saved
AVIFYes≈92%
WEBPYes≈88%
JPGYes≈85%
PNGYes≈68%
SVGYes≈8%
ico🚧 Under Development-
💡

The compression effect is based on statistics, the actual effect may vary, and this table will be updated continuously.

History

The images compressed by the plugin will be permanently stored on your computer until you clear the cache or delete the history.

Due to client storage limitations, we only save 15 history records.

Free Image Hosting Service

ExportX provides you with a free image hosting service that allows for quick uploads. The service is built on Cloudflare’s free CDN, and we guarantee not to delete your images. This allows you to get an upload experience with zero configuration.

Turn on the switch in the upper right corner of the plugin to use our free image hosting service.

At this point, click the “ExportX” button to complete the compression and upload operation in one click. In the history record, click the copy button to get the image link.

You need to comply with our relevant agreement to use our free image hosting service.

Of course, we recommend that you use a custom upload service.

Custom Upload Service

If you have your own object storage service, you can replace it with your own upload interface. First, click [Settings] in the upper left corner, and select “Custom” in the upload settings.

ImageImage

Upload Interface Specification

After enabling custom upload, the image will be uploaded to your interface via a POST request. The interface needs to comply with the following specifications:

Request Content

Header: You can pass tokens and other information through custom headers.

Content-Type: multipart/form-data;

Body: The plugin uploads files in form-data format. The following parameters are provided by the plugin. At the same time, you can pass additional information through custom Body.

Parameter NameTypeDescription
fileFileBinary file
fileNameStringFile name, such as image92.avif
formatStringImage format, such as png
figmaNameStringFigma username, users can modify it in Figma, it is not unique but readable.
figmaIdStringFigma user ID, a 19-digit number, refer to Figma User
xidStringComposite field, the format is figmaName@figmaId

Response Content

You need to return a json object to the plugin, which contains the url of the image. The url is used to copy to the clipboard. If the url is not returned, the copy button will be unavailable.

{
  "url": "https://dev.cdn.abfree.com/images/6FjJoXmt34ox0Fu1.png"
}

Cloudflare R2+Worker

Sample code, here using hono for demonstration

import { Hono } from "hono";

type Bindings = {
  MY_BUCKET: R2Bucket;
  CDN_URL: string;
};

const app = new Hono<{
  Bindings: Bindings;
}>();

app.post("/upload", async (c) => {
  const key = genFileKey();
  const formData = await c.req.parseBody();
  const file = formData["file"];

  if (file instanceof File) {
    const fileBuffer = await file.arrayBuffer();
    const fullName = file.name;
    const ext = fullName.split(".").pop();
    const path = `images/${key}.${ext}`;

    let contentType = file.type;

    if (contentType === "image/svg") {
      contentType = "image/svg+xml";
    }

    console.log(file.type);
    await c.env.MY_BUCKET.put(path, fileBuffer, {
      httpMetadata: {
        contentType: contentType,
        contentDisposition: `inline`,
      },
    });
    return c.json({
      url: `${c.env.CDN_URL}/${path}`,
    });
  } else {
    return c.text("Invalid file", 400);
  }
});

Amazon S3 or S3 Compatible

Most object storage is compatible with the S3 protocol, such as

  • AWS S3
  • Cloudflare R2
  • Google Cloud Storage
  • Alibaba Cloud OSS
  • Tencent Cloud COS

Sample code

// api.ts
import { Hono } from "hono";
import { uploadToS3 } from "./s3";

type Bindings = {
  CDN_URL: string;
};

const app = new Hono<{
  Bindings: Bindings;
}>();

app.post("/uploadS3", async (c) => {
  const key = genFileKey();
  const formData = await c.req.parseBody();
  const file = formData["file"];
  if (file instanceof File) {
    const fileBuffer = await file.arrayBuffer();
    const fullName = file.name;
    const ext = fullName.split(".").pop();
    const path = `images/${key}.${ext}`;
    let contentType = file.type;
    if (contentType === "image/svg") {
      contentType = "image/svg+xml";
    }
    const uint8Array = new Uint8Array(fileBuffer);
    await uploadToS3(c, {
      file: uint8Array,
      contentType,
      key: key,
    });
    return c.json({
      url: `${c.env.CDN_URL}/${path}`,
    });
  } else {
    return c.text("Invalid file", 400);
  }
});
// s3.ts
import { S3 } from "@aws-sdk/client-s3";
import { Context } from "hono";

let s3Client: S3;
interface UploadParams {
  file: Uint8Array;
  contentType: string;
  key: string;
}
export async function uploadToS3(c: Context, params: UploadParams) {
  const client = getClient(c);
  await client.putObject({
    Body: params.file,
    Bucket: c.env.S3_BUCKET,
    Key: params.key,
    ContentType: params.contentType,
  });
}
function getClient(c: Context) {
  if (s3Client) return s3Client;
  const { S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_ENDPOINT, S3_REGION } =
    c.env;
  s3Client = new S3({
    credentials: {
      accessKeyId: S3_ACCESS_KEY_ID,
      secretAccessKey: S3_SECRET_ACCESS_KEY,
    },
    endpoint: S3_ENDPOINT,
    region: S3_REGION,
  });
  return s3Client;
}

QA

How is the upload initiated?

To protect the privacy of your images, the upload action is initiated by the front end, so you may need to handle cross-domain issues.

How to avoid service abuse

In general, the upload service interface you fill in is publicly accessible. If the interface is leaked, this may lead to anonymous access or even abuse. Therefore, we suggest you use xid+secret whitelist to restrict access.

What is xid?

xid is a composite field of figmaId and figmaName, the format is figmaName@figmaId. It is included in the uploaded body, and you can verify this field in the interface to see if it is in the whitelist.

  • figmaName, is the user’s name in Figma login information, users can modify it in Figma, it is not unique but readable.
  • figmaId, a 19-digit number, is the unique identifier of the user in Figma login information.

You can refer to [Figma User]

xid is difficult to forge and is already secure enough, but if you need higher security, we suggest you add a secret field.

What is secret?

secret is a password you set yourself. You can generate it yourself and issue it to users. Users need to fill in this password in the Body when using the plugin.

💡

Of course, this is just a suggestion, the specific security strategy needs to be formulated according to your actual situation.

How to troubleshoot upload errors

If you use Figma in your browser, you can check the Network or Console panel in the developer tools to see the upload request and return.

If you are using the Figma client, you can open the console to get error information according to the following prompts.

How to get my xid

On the plugin homepage, click the user button in the upper right corner, and you can see your xid.