How to Generate Stylish PDFs and Images with Tailwind CSS in Your Application
Published: 2024-04-17 18:40:08

**UPDATE 2022-05-22:** tailwindstream api got some improvements. Please follow the updated stackblitz example that you can find on [tailwindstream.io ](https://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](https://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](https://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**](https://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**](https://tailwindstream.io) API to generate a document:

```typescript
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;
```

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

```typescript
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();
}
```

Handle download attempts and retries:

```typescript
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);
}
```

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:

```typescript
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
  };
};
```

## **Conclusion**

Integrating Tailwind CSS with the [tailwindstream.io](http://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](http://tailwindstream.io).

Happy coding!