Skip to content

Component Library Support ​

EasyPdf captures the live DOM exactly as the browser renders it. This means any styling the browser applies — regardless of how it was injected — appears in the PDF. CSS classes, inline styles, CSS-in-JS (Emotion, styled-components), Tailwind utilities, MUI theme tokens — all of it.

There is no configuration required. Just render your components and call downloadPDF.

Tailwind CSS ​

Tailwind classes are applied to the DOM as computed styles, which EasyPdf captures directly. No special setup needed.

tsx
import { useEasyPdf } from "@easypdf/react";

function Report() {
  const { pdfRef, downloadPDF, isLoading } = useEasyPdf();

  return (
    <div>
      <button onClick={() => downloadPDF(pdfRef)} disabled={isLoading}>
        {isLoading ? "Generating..." : "Download PDF"}
      </button>

      <div ref={pdfRef} className="max-w-2xl mx-auto p-8 font-sans bg-white">
        <h1 className="text-3xl font-bold text-gray-900 mb-2">Sales Report</h1>
        <p className="text-sm text-gray-500 mb-6">Q1 2024</p>

        <div className="grid grid-cols-3 gap-4 mb-8">
          {[
            { label: "Revenue", value: "$84,254" },
            { label: "Users", value: "3,842" },
            { label: "Churn", value: "2.1%" },
          ].map((stat) => (
            <div key={stat.label} className="border border-gray-200 rounded-lg p-4">
              <p className="text-xs text-gray-500 uppercase tracking-wide">{stat.label}</p>
              <p className="text-2xl font-semibold text-gray-900 mt-1">{stat.value}</p>
            </div>
          ))}
        </div>

        <table className="w-full text-sm">
          <thead>
            <tr className="border-b border-gray-200">
              <th className="text-left py-2 text-gray-500 font-medium">Product</th>
              <th className="text-right py-2 text-gray-500 font-medium">Revenue</th>
            </tr>
          </thead>
          <tbody>
            <tr className="border-b border-gray-100">
              <td className="py-2 text-gray-900">Pro License</td>
              <td className="py-2 text-right font-medium text-gray-900">$62,000</td>
            </tr>
            <tr className="border-b border-gray-100">
              <td className="py-2 text-gray-900">Enterprise Suite</td>
              <td className="py-2 text-right font-medium text-gray-900">$22,254</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  );
}

MUI (Material UI) ​

MUI v5 uses Emotion for CSS-in-JS. EasyPdf resolves all Emotion-injected styles at capture time via getComputedStyle, so MUI components render with full layout, color, and typography in the PDF.

tsx
import { useEasyPdf } from "@easypdf/react";
import {
  Box, Typography, Paper, Table, TableBody,
  TableCell, TableContainer, TableHead, TableRow, Chip,
} from "@mui/material";

function MuiReport() {
  const { pdfRef, downloadPDF, isLoading } = useEasyPdf({
    pageSize: "A4",
    margins: { top: 20, right: 20, bottom: 20, left: 20 },
  });

  const rows = [
    { product: "Pro License", revenue: "$62,000", status: "Active" },
    { product: "Enterprise Suite", revenue: "$22,254", status: "Active" },
    { product: "Starter Kit", revenue: "$13,140", status: "Pending" },
  ];

  return (
    <div>
      <button onClick={() => downloadPDF(pdfRef)} disabled={isLoading}>
        {isLoading ? "Generating..." : "Download PDF"}
      </button>

      <Box ref={pdfRef} sx={{ p: 4, bgcolor: "#fafafa" }}>
        <Paper elevation={2} sx={{ p: 3, mb: 3, bgcolor: "#1976d2", color: "white" }}>
          <Typography variant="h5">Sales Report</Typography>
          <Typography variant="body2" sx={{ opacity: 0.85, mt: 0.5 }}>Q1 2024</Typography>
        </Paper>

        <Paper variant="outlined" className="no-break">
          <TableContainer>
            <Table size="small">
              <TableHead>
                <TableRow sx={{ bgcolor: "#fafafa" }}>
                  {["Product", "Revenue", "Status"].map((h) => (
                    <TableCell key={h} sx={{ fontWeight: 600, fontSize: "0.75rem", color: "#9e9e9e", textTransform: "uppercase" }}>
                      {h}
                    </TableCell>
                  ))}
                </TableRow>
              </TableHead>
              <TableBody>
                {rows.map((row) => (
                  <TableRow key={row.product}>
                    <TableCell>{row.product}</TableCell>
                    <TableCell sx={{ fontWeight: 500 }}>{row.revenue}</TableCell>
                    <TableCell>
                      <Chip
                        label={row.status}
                        size="small"
                        color={row.status === "Active" ? "success" : "warning"}
                        variant="outlined"
                      />
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
          </TableContainer>
        </Paper>
      </Box>
    </div>
  );
}

shadcn/ui ​

shadcn/ui components use Tailwind CSS and CSS custom properties (CSS variables) for theming. EasyPdf copies all CSS variables from :root into the capture context, so shadcn themes and colors render correctly.

tsx
import { useEasyPdf } from "@easypdf/react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";

function ShadcnReport() {
  const { pdfRef, downloadPDF, isLoading } = useEasyPdf();

  return (
    <div>
      <button onClick={() => downloadPDF(pdfRef)} disabled={isLoading}>
        {isLoading ? "Generating..." : "Download PDF"}
      </button>

      <div ref={pdfRef} className="p-8 bg-background">
        <h1 className="text-2xl font-semibold tracking-tight text-foreground mb-6">
          Sales Report
        </h1>

        <div className="grid grid-cols-3 gap-4 mb-6">
          {[
            { label: "Total Revenue", value: "$84,254" },
            { label: "Active Users", value: "3,842" },
            { label: "Churn Rate", value: "2.1%" },
          ].map((stat) => (
            <Card key={stat.label} className="no-break">
              <CardHeader className="pb-2">
                <p className="text-sm text-muted-foreground">{stat.label}</p>
              </CardHeader>
              <CardContent>
                <p className="text-2xl font-bold">{stat.value}</p>
              </CardContent>
            </Card>
          ))}
        </div>

        <Card className="no-break">
          <CardHeader>
            <CardTitle>Recent Orders</CardTitle>
          </CardHeader>
          <CardContent>
            <div className="space-y-2">
              {[
                { name: "Olivia Martin", amount: "+$1,999", status: "Success" },
                { name: "Jackson Lee", amount: "-$39", status: "Processing" },
                { name: "Isabella Nguyen", amount: "+$299", status: "Success" },
              ].map((row) => (
                <div key={row.name} className="flex items-center justify-between py-1">
                  <span className="text-sm font-medium">{row.name}</span>
                  <div className="flex items-center gap-3">
                    <Badge variant={row.status === "Success" ? "default" : "secondary"}>
                      {row.status}
                    </Badge>
                    <span className="text-sm font-semibold">{row.amount}</span>
                  </div>
                </div>
              ))}
            </div>
          </CardContent>
        </Card>
      </div>
    </div>
  );
}

Tips for All Libraries ​

Prevent elements from splitting across pages ​

Add the no-break class to any element that should always stay on one page:

tsx
<Card className="no-break">
  {/* This card will never be split across pages */}
</Card>

The following element types are automatically treated as unbreakable: <table>, <figure>, <img>, <pre>, <code>, <ul>, <ol>.

Fix layout width ​

Pin a container width so the PDF layout is consistent regardless of the browser window size:

tsx
const { pdfRef, downloadPDF } = useEasyPdf({
  container: {
    style: { width: "800px" },
  },
});

External images ​

Add crossOrigin="anonymous" to any <img> that loads from a different domain:

tsx
<img src="https://example.com/logo.png" crossOrigin="anonymous" alt="Logo" />

Released under the MIT License.