DEV Community

Cover image for How to Generate Stylish PDFs and Images with Tailwind CSS in Your Application
kstulgys
kstulgys

Posted on • Updated on

How to Generate Stylish PDFs and Images with Tailwind CSS in Your Application

UPDATE 2022-05-22: tailwindstream api got some improvements. Please follow the updated stackblitz example that you can find on tailwindstream.io

Transform your application into a powerhouse for creating stunning documents and images using HTML styled with Tailwind CSS. In this guide, we’ll walk you through the process of integrating a powerful document generation API from tailwindstream.io, showcasing how you can enhance your application's functionality with high-quality, styled document outputs.

Why Use Tailwind CSS for Document Generation?

Tailwind CSS is renowned for its utility-first approach, making it an excellent choice for developers looking to implement custom styles quickly without leaving the comfort of their HTML. By using Tailwind CSS with the tailwindstream.io API, developers can ensure that their digital documents carry the same level of precision and customisation as their web interfaces.

Integrating the tailwindstream.io API

Our chosen API isn't just any document generation tool; it excels by accepting HTML content styled using Tailwind CSS, allowing full utilization of Tailwind's utility classes directly within your documents. Here’s a brief overview of the API capabilities that you can leverage:

  • HTML Content: Embed HTML with Tailwind CSS for dynamic styling.

  • Format Specification: Supports various paper sizes such as A4, Letter, etc.

  • Output Options: Generate documents in PDF, PNG, JPEG, or WebP formats.

  • Custom Dimensions: Control output dimensions and scaling to ensure your documents look perfect.

Below is a basic React component setup that utilises the tailwindstream.io API to generate a document:

import { useState } from 'react';

const sampleHtml = `
<figure class="md:flex bg-slate-100 rounded-xl p-8 md:p-0 dark:bg-slate-800">
  <img class="w-24 h-24 md:w-48 md:h-auto md:rounded-none rounded-full mx-auto object-cover" src="https://tailwindcss.com/_next/static/media/sarah-dayan.de9b3815.jpg" alt="" width="384" height="512">
  <div class="pt-6 md:p-8 text-center md:text-left space-y-4">
    <blockquote>
      <p class="text-lg font-medium">
        “Tailwind CSS is the only framework that I've seen scale
        on large teams. It’s easy to customize, adapts to any design,
        and the build size is tiny.”
      </p>
    </blockquote>
    <figcaption class="font-medium">
      <div class="text-sky-500 dark:text-sky-400 font-bold">
        Sarah Dayan
      </div>
      <div class="text-slate-700 dark:text-slate-500">
        Staff Engineer, Algolia
      </div>
    </figcaption>
  </div>
</figure>
`;

function App() {
  const [error, setError] = useState('');
  const [downloading, setDownloading] = useState(false);
  const [payload, setPayload] = useState<BodyPayload>({
    html: sampleHtml,
    output: 'pdf',
  });

  const setHtml = (htmlContent: string) => {
    setPayload({ ...payload, html: htmlContent });
  };

  const setOutput = (outputFormat: BodyPayload["output"]) => {
    setPayload({ ...payload, output: outputFormat });
  };

  const download = async () => {
    setDownloading(true);
    try {
      const response = await requestDownload(payload);
      if (response.error) {
        setError(response.error);
        setDownloading(false);
      } else if (response.requestId) {
        downloadWithRetry(response.requestId);
      }
    } catch (error) {
      setError('Something went wrong.');
      setDownloading(false);
    }
  };

  return (
    <div>
      <SelectOutput onChange={setOutput} />
      <button onClick={download}>{downloading ? 'Downloading...' : `Download .${payload.output}`}</button>
      {error && <p>{error}</p>}
      <textarea
        rows={25}
        cols={80}
        value={payload.html}
        onChange={(e) => setHtml(e.target.value)}
      />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Send a POST request with your styled HTML content to generate the desired file format:

const apiUrl = `https://api.tailwindstream.io`;

async function requestDownload(payload: BodyPayload) {
  const response = await fetch(apiUrl + '/request', {
    method: 'POST',
    body: JSON.stringify(payload),
    headers: { 'Content-Type': 'application/json' },
  });
  return await response.json();
}
Enter fullscreen mode Exit fullscreen mode

Handle download attempts and retries:

const RETRY_INTERVAL_MS = 2500;

async function downloadWithRetry(requestId: string) {
  const intervalId = setInterval(async () => {
    const response = await fetch(`${apiUrl}/request/${requestId}/download`);
    if (response.ok) {
      const blob = await response.blob();
      downloadToBrowser(blob);
      clearInterval(intervalId);
    } else {
      console.error('Download failed, retrying...');
    }
  }, RETRY_INTERVAL_MS);
}
Enter fullscreen mode Exit fullscreen mode

Document generation might take 2-6 second. This retry logic ensures we download the file when it's ready.

You might ask: what are the options I can pass to the api? Well... here is the typescript definition my friend:

type BodyPayload = {
  html?: string; // must be undefined if 'template' prop is used
  format?: // applicable only for pdf, default a4
    | "LETTER"
    | "LEGAL"
    | "TABLOID"
    | "LEDGER"
    | "A0"
    | "A1"
    | "A2"
    | "A3"
    | "A4"
    | "A5"
    | "A6"
    | "Letter"
    | "Legal"
    | "Tabloid"
    | "Ledger"; 
  output?: "pdf" | "png" | "jpeg" | "webp"; // default pdf
  size?: {
    scale?: number; // default 2
    width?: string | number; // default 210
    height?: string | number; // default 297
    unit?: "px" | "in" | "cm" | "mm"; // default mm
  };
  template?: { 
    html: string; // handlebars dynamic html 
    data: Record<string, any>; // data for dynamic html
  };
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

Integrating Tailwind CSS with the tailwindstream.io API in your application not only enhances the consistency between web and printable formats but also simplifies the process of generating custom-styled documents. Start utilizing this integration today and take your application's functionality to the next level. For more information visit tailwindstream.io.

Happy coding!

Top comments (0)