DEV Community

Henry Kehlmann
Henry Kehlmann

Posted on • Updated on

PDF Server (for Figma templates) STEP 1

Idea

I'm building a SaaS for a client and one part of a critical functionality is the ability to export dynamic reports with all kinds of data in PDFs.
Currently the layouts and styles for PDFs are hardcoded in code and any updates need code changes. But what if a designer could just edit a template in Figma, test it out and voilà the new design is applied in production as well without any code changes.

How

Using Next.js and React-PDF to preview / stream the PDF template based on defined properties onto Figma template files

Probably need to implement some definitions to allow automatically repeated components, calculated/dynamic content, custom fonts etc but let's keep it simple for now.

Let's start up the project

I decided to generate a new boilerplate next.js project using the typescript + chakra example. We can use Chakra for any of the UI elements.

npx create-next-app --example with-chakra-ui-typescript pdf-server
Enter fullscreen mode Exit fullscreen mode

Architecture

Alt Text

As you can see we need to install figma-js and react-pdf and start integrating these 2 together to create an api endpoint.

yarn add @react-pdf/renderer figma-js figma-transformer
Enter fullscreen mode Exit fullscreen mode

figma-transformer helps us process the response from figma api by creating .shortcuts in every layer/type.

utils/getFigmaFile.ts

This allows us to query all the frames in a figma template and their children and properties. Implementing a number of env vars helps us to customize the server depending on the use case. MAX_VECTORS tries to help safeguard the server from loading way too large figma files with vectors which could lead to memory/crash issues.

import * as Figma from 'figma-js'
import { processFile } from 'figma-transformer'

const client = Figma.Client({
  personalAccessToken: process.env.FIGMA_TOKEN
})

export const DEFAULT_FILE = process.env.DEFAULT_FILE || `w4qFtzyCX2fYT3x6CQDFQF`
export const MAX_VECTORS = process.env.MAX_VECTORS || 100
export const ALLOWED_FILES = process.env.NEXT_PUBLIC_ALLOWED_FILES as string
export const allowedFiles = ALLOWED_FILES.length ? ALLOWED_FILES?.split(',') : []

type FigmaOptions = {
  geometry?: 'paths' | ''
}

export const getFigmaFile = async (fileId: string, options?: FigmaOptions) => {
  if (allowedFiles.length && !allowedFiles.includes(fileId)) {
    throw new Error('This file id is not allowed by the server env ALLOWED_FILES')
  }
  const figmaFile = await client.file(fileId, options ?? {
    geometry: 'paths'
  }).then(({ data }) => data )
  console.log('figma children: ', figmaFile.document.children.length)
  const file = processFile(figmaFile)
  console.log('figma processed vectors length: ', file.shortcuts.vectors?.length)
  if (file.shortcuts.vectors && file.shortcuts.vectors.length > MAX_VECTORS) {
    throw new Error('Too many vectors in template')
  }
  return file
}

Enter fullscreen mode Exit fullscreen mode

Now we need to start mapping the file response elements into a react-pdf document.

Continue to part 2

Top comments (0)