oxhq / canio
Browser-grade PDF and document rendering for Laravel powered by Stagehand
Requires
- php: ^8.2
- illuminate/console: ^10.0|^11.0|^12.0|^13.0
- illuminate/filesystem: ^10.0|^11.0|^12.0|^13.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- symfony/process: ^6.4|^7.0|^8.0
Requires (Dev)
- laravel/pint: ^1.23
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- pestphp/pest: ^2.34|^3.8|^4.0
- pestphp/pest-plugin-laravel: ^2.4|^3.2|^4.0
README
oxhq/canio is the Laravel-facing package for Canio.
It keeps the API Laravel-native and delegates execution to the Stagehand runtime. In the default embedded mode, that runtime is installed and started automatically when the package needs it.
Supported Versions
- PHP
^8.2 - Laravel
^10.0 | ^11.0 | ^12.0 | ^13.0
Install
composer require oxhq/canio php artisan canio:install
Public docs: oxhq.github.io/canio
The second command is recommended for deployment and validation because it:
- publishes the default config
- downloads the matching Stagehand binary
- downloads the Chrome for Testing browser bundle for local CDP rendering
- verifies release checksums
- runs a local doctor check
The package can also auto-install and auto-start the runtime on first render in embedded mode, but explicit install is the cleaner production path.
If you need the config file:
php artisan vendor:publish --tag=canio-config
Quick Start
use Oxhq\Canio\Facades\Canio; return Canio::view('pdf.invoice', ['invoice' => $invoice]) ->profile('invoice') ->title('Invoice #123') ->stream('invoice.pdf');
Supported entrypoints:
Canio::view(...)Canio::html(...)Canio::url(...)
Common terminal operations:
->render()->save(...)->download(...)->stream(...)->dispatch()
When To Choose Canio
Choose Canio when you need:
- real browser layout
- JavaScript execution before capture
- explicit readiness with
window.__CANIO_READY__ - render artifacts for debugging
- async render jobs and runtime operations
Do not choose Canio only because you want the smallest possible cold-render time on a static HTML invoice. That is not the category this package is trying to win.
Runtime Model
Embedded mode
embedded is the default and recommended package experience.
- Laravel talks to Stagehand through the package
- the runtime is installed automatically when missing
- the runtime is auto-started on demand
- the application does not need a manually managed daemon in the happy path
Remote mode
Use remote when you want Laravel to talk to an already-running Stagehand daemon.
Useful config keys:
runtime.moderuntime.base_urlruntime.auto_installruntime.auto_startruntime.binaryruntime.install_pathruntime.startup_timeout
Production deployment guide: embedded vs remote runtimes
Troubleshooting
If the first render fails, check these first:
- Run
php artisan canio:doctor - Confirm
php artisan canio:installsucceeded - Confirm
php artisan canio:browser:installsucceeded, or setCANIO_CHROMIUM_PATH - In locked-down Linux environments, you may also need
CANIO_CHROMIUM_NO_SANDBOX=true
Stagehand uses the Rod-backed native renderer by default:
CANIO_RENDERER_DRIVER=rod-cdp CANIO_BROWSER_PRODUCT=chrome CANIO_BROWSER_CHANNEL=Stable CANIO_BROWSER_INSTALL_PATH=/var/lib/canio/browsers
chrome is the default browser product because Canio optimizes for browser-real PDF fidelity. chrome-headless-shell is available for controlled environments that prefer the lighter old-headless shell tradeoff:
CANIO_BROWSER_PRODUCT=chrome-headless-shell
To fall back to the direct CDP renderer without changing Laravel integration code:
CANIO_RENDERER_DRIVER=local-cdp CANIO_BROWSER_INSTALL_PATH=/var/lib/canio/browsers
Useful browser bundle commands:
php artisan canio:browser:install php artisan canio:browser:install --product=chrome-headless-shell php artisan canio:browser:install 123.0.6312.86 --platform=linux64 php artisan canio:browser:repair
If the app server should use an external Chrome/CDP service instead of launching local Chromium:
CANIO_RENDERER_DRIVER=remote-cdp CANIO_REMOTE_CDP_ENDPOINT=ws://chrome-renderer.internal:9222/devtools/browser/<id>
If you want a self-hosted runtime instead of embedded mode:
CANIO_RUNTIME_MODE=remote CANIO_RUNTIME_BASE_URL=http://127.0.0.1:9514
Benchmarks And Proof
Canio is positioned as the browser-grade option, not the minimum-latency static option.
The checked-in harnesses currently establish:
- Canio is the most faithful engine on the reference invoice fixture
- Canio beats Browsershot and Snappy on useful performance in that lane
- Canio executes runtime JavaScript correctly in the probe harness
- Dompdf and mPDF still win on raw uncached latency for simpler static renders
Public benchmark summary: oxhq/canio benchmark summary
Full harnesses: oxhq/canio benchmarks
Optional Cloud Layer
This package works without any cloud dependency.
Cloud is an optional paid layer on top of Canio OSS. Keep it secondary in the package story: local or self-hosted rendering is fully supported without it.
Commands And Operations
The package also exposes runtime and job commands such as:
php artisan canio:installphp artisan canio:doctorphp artisan canio:servephp artisan canio:runtime:statusphp artisan canio:runtime:job {id}php artisan canio:runtime:artifact {id}php artisan canio:runtime:cleanup
The facade also exposes job and artifact helpers, including:
Canio::job($jobId)Canio::jobs($limit = 20)Canio::artifact($artifactId)Canio::artifacts($limit = 20)Canio::retryJob($jobId)Canio::cancelJob($jobId)Canio::replay($artifactId)
More Documentation
- Public docs: oxhq.github.io/canio
- Monorepo overview: oxhq/canio
- Production deployment guide: deployment guide
- Contributor setup: development guide
- Architecture notes: architecture
- Render contract: render contract