ScreenshotNeo

BlogHTML to image & PDF

How to Convert HTML to PDF with Pagination in Yii2

Render a Yii2 view to paginated PDF with mPDF, CSS page breaks, headers, footers, troubleshooting, and a no-browser ScreenshotNeo option.

By the ScreenshotNeo team30 September 20267 min read

How to Convert HTML to PDF with Pagination in Yii2

Short answer: render your Yii2 view as print-oriented HTML, pass it to an mPDF integration, and control sheet breaks with AddPage(), <pagebreak />, or CSS such as page-break-before: always. Yii2 data pagination is a different concern: yii\data\Pagination chooses which records appear in an application page, while mPDF pagination places a continuous document onto PDF sheets.

1. Decide which pagination you need

Requirement Use
Show 20 database records per request Yii2 yii\data\Pagination, usually with a data provider and grid/list view
Put a long invoice or report onto Letter/A4 sheets mPDF page breaks and print CSS
Start every chapter on a new sheet CSS page-break-before, HTML <pagebreak />, or PHP AddPage()

This guide covers document pagination. If you also need database pagination, finish the query and data-provider pagination first, then render the resulting report HTML to PDF.

2. Confirm the renderer and versions

The title does not identify a PDF engine. The examples below use mPDF through a Yii2 integration because mPDF accepts HTML and supports CSS and page controls. Kartik’s yii2-mpdf catalog entry is historical, so verify the package’s current compatibility with your PHP, Yii2, and mPDF versions before installing it. Other Yii2 routes include an mPDF response formatter and a wkhtmltopdf-based converter; the latter requires the wkhtmltopdf binary on the deployment host.

3. Install and configure an mPDF integration

Use the package documentation that matches your project and lock compatible versions in Composer. A typical Yii2 component configuration has this shape:

<?php
// config/web.php
return [
    'components' => [
        'pdf' => [
            'class' => 'kartik\\mpdf\\Pdf',
            'format' => 'A4',
            'orientation' => 'P',
            'destination' => 'D',
            'content' => '',
            'cssFile' => '@app/web/css/print.css',
            'cssInline' => '',
            'options' => [
                'title' => 'Report',
            ],
            'methods' => [
                'SetHeader' => ['Report'],
                'SetFooter' => ['Page {PAGENO}'],
            ],
        ],
    ],
];

Configuration keys differ between integrations and releases. Treat this as a pattern: check the installed extension’s API for the exact component class, destination values, and option names.

4. Render a Yii2 view and return the PDF

Create a print view without the normal site layout. Keep the HTML semantic and give tables predictable widths.

A Yii2 view becomes a paginated PDF through the renderer.
A Yii2 view becomes a paginated PDF through the renderer.
<?php
// controllers/ReportController.php
namespace app\\controllers;

use Yii;
use yii\\web\\Controller;

class ReportController extends Controller
{
    public function actionPdf($id)
    {
        $report = $this->findReport($id);
        $html = $this->renderPartial('pdf', [
            'report' => $report,
        ]);

        $pdf = Yii::$app->pdf;
        $pdf->content = $html;
        return $pdf->render();
    }

    private function findReport($id)
    {
        return Report::find()->with('items')->where(['id' => $id])->one();
    }
}

The corresponding view can contain a deliberate section break:

<h1><?= htmlspecialchars($report->title, ENT_QUOTES, 'UTF-8') ?></h1>
<section class='summary'>
    <!-- summary fields -->
</section>

<div class='chapter'>
    <h2>Line items</h2>
    <table class='items'>
        <thead><tr><th>Description</th><th>Amount</th></tr></thead>
        <tbody>
        <?php foreach ($report->items as $item): ?>
            <tr>
                <td><?= htmlspecialchars($item->description, ENT_QUOTES, 'UTF-8') ?></td>
                <td><?= number_format($item->amount, 2) ?></td>
            </tr>
        <?php endforeach; ?>
        </tbody>
    </table>
</div>

Escape user-controlled text, resolve images to paths or data that the renderer can read, and avoid relying on browser-only JavaScript to build the document.

5. Add page breaks with CSS, HTML, or PHP

CSS break before a section

.chapter {
    page-break-before: always;
}

.keep-together {
    page-break-inside: avoid;
}

@page {
    size: A4;
    margin: 18mm 15mm 20mm;
}

page-break-before: always starts the element on a new sheet. CSS left and right values can request an even or odd page position where supported. page-break-inside: avoid is a best-effort hint, not a guarantee: mPDF documents limits for blocks larger than two pages and incompatibilities with table autosizing or rotation.

Hard section breaks and table layout determine where PDF content continues.
Hard section breaks and table layout determine where PDF content continues.

HTML pagebreak control

<h2>Next section</h2>
<pagebreak />
<h2>Starts on a new PDF page</h2>

mPDF’s HTML control can also change margins, orientation, headers, footers, and page-number behavior from the new page onward. A forced break may close enclosing block elements, so do not depend on a wrapper’s styling continuing across the break.

PHP page advance

<?php
$mpdf->AddPage();
$mpdf->WriteHTML($html);
$mpdf->Output();

Use PHP breaks when your controller or report builder knows the exact boundary. Use CSS or HTML when the boundary belongs naturally in the view.

6. Headers, footers, margins, orientation, and numbering

Set page dimensions and margins in the renderer configuration or with @page. If @page supplies margins, those values override margins passed to the mPDF constructor. Define named headers and footers when the integration exposes them, and decide whether page numbers should continue, reset, change style, or be suppressed at section boundaries.

@page {
    size: A4 landscape;
    margin: 14mm 12mm 18mm;
}

body { font-family: sans-serif; font-size: 10pt; }
header { position: running(report-header); }
footer { position: running(report-footer); }

Exact running-header behavior depends on the mPDF version and integration. Validate the generated document rather than assuming browser print CSS and mPDF CSS behave identically.

7. Tables, images, fonts, and long content

  • Use explicit column widths for financial or tabular reports.
  • Repeat table headings through the renderer’s table-header behavior where available.
  • Keep very large rows from becoming single unbreakable blocks.
  • Use print-sized images and a font available to the renderer; missing fonts can change line wrapping and therefore page count.
  • Expect automatic breaks to differ from browser print preview. mPDF documents limited control over automatic breaks and no widows/orphans protection.

Always inspect long tables, content near boundaries, section starts, headers, footers, and pages containing large images.

8. A complete minimal mPDF example

<?php
require __DIR__ . '/vendor/autoload.php';

$mpdf = new \\Mpdf\\Mpdf([
    'format' => 'A4',
    'orientation' => 'P',
    'margin_top' => 20,
    'margin_bottom' => 20,
]);

$css = file_get_contents(__DIR__ . '/web/css/print.css');
$html = file_get_contents(__DIR__ . '/views/report.html');
$mpdf->WriteHTML($css, \\Mpdf\\HTMLParserMode::HEADER_CSS);
$mpdf->WriteHTML($html, \\Mpdf\\HTMLParserMode::HTML_BODY);
$mpdf->Output('report.pdf', 'D');

Adapt namespaces and output mode to the mPDF version installed by your Yii2 extension.

9. Alternatives to mPDF

Route What to verify
Kartik yii2-mpdf HTML content, CSS file/inline CSS, mPDF options, and compatibility with current PHP/Yii2 versions
Yii2 PDF response formatter Controller response configuration and the mPDF version it wraps
wkhtmltopdf converter Installed binary, executable path, page-size options, headers, and deployment permissions

No source establishes one universally best renderer. Choose based on CSS behavior, fonts and languages, page-break needs, external dependencies, and maintenance status.

10. Troubleshooting

Break is ignored

Check that the CSS reaches mPDF, the selector matches the element, and the element is not inside malformed HTML. Try <pagebreak /> or AddPage() for a known hard boundary.

A section loses its styling after a break

mPDF may close enclosing blocks at a forced break. Move required styles onto the content after the break or use a pagebreak control that sets the needed page properties.

A table splits in an inconvenient place

Add page-break-inside: avoid to small blocks, reduce oversized rows, and use explicit widths. The rule has documented limits and cannot guarantee arbitrary tables stay intact.

Margins do not match configuration

Inspect @page; its margins can override constructor or component margins.

PDF is blank or missing images

Confirm the view rendered HTML, image paths are readable by the PHP process, and remote-resource access is configured for your renderer. Log the final HTML during development.

Fonts change the page count

Install and register the intended fonts in the renderer, then regenerate. Different metrics alter line wrapping and break positions.

Composer or binary errors appear in production

Check PHP extension compatibility and the lockfile for mPDF integrations. For wkhtmltopdf, verify the binary exists at the configured path and is executable by the web or queue user.

11. Performance, reliability, and cost

  • Render only the data required for the report; eager-load related records to avoid query-per-row behavior.
  • Reuse print CSS and avoid oversized images; both reduce memory and output time.
  • Generate large reports in a queue and store the resulting file instead of holding a long HTTP request open.
  • Set request timeouts appropriate to report size and log renderer errors with the report identifier.
  • Keep renderer and font versions pinned, and compare page counts after upgrades.
  • PDF generation consumes server CPU and memory; estimate capacity from your own document sizes because the supplied sources provide no universal benchmark.

12. Or skip the browser setup

If your actual requirement is a screenshot or PDF of a URL rather than server-side Yii2 HTML rendering, ScreenshotNeo provides a single request API and an MCP server for AI agents. See the ScreenshotNeo documentation for request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

13. FAQ

Can Yii2 pagination create PDF pages automatically?

Yii2 data pagination selects records. The PDF renderer determines physical page breaks after the HTML is rendered.

Which break method should I choose?

Use CSS for view-level section rules, <pagebreak /> for mPDF-specific HTML control, and AddPage() when PHP determines the boundary.

Does page-break-inside: avoid guarantee no split?

No. It is a constrained best-effort hint and does not provide widows/orphans protection.

Can I switch from mPDF to wkhtmltopdf without changing views?

Often you can reuse much of the HTML, but CSS support, JavaScript behavior, fonts, headers, and page-break handling differ. Recheck every report.