HTML to Image in Laravel

Render HTML to a PNG from Laravel using the Http facade and queueable jobs.

Best for: Laravel apps generating invoices on payment, OG images on post publish, or any background image render that benefits from queue retries.

Worked examples covering the Http facade, queue integration with Active Job patterns, and storing returned URLs on any disk.

Setup

Add your API key to your .env file:

HTML2IMG_API_KEY=your-api-key

API Response Format

Every successful request returns a JSON body with a url pointing to the generated image:

{
    "success": true,
    "credits_remaining": 95,
    "id": "abc123",
    "url": "https://i.html2img.com/abc123.png"
}

You can either return the url directly to the client, or download the image from it if you need a local copy.

HTML to Image Example

Here’s a simple example of converting HTML to an image using Laravel’s HTTP facade:

use Illuminate\Support\Facades\Http;

public function generateImage()
{
    $response = Http::withHeaders([
        'X-API-Key' => env('HTML2IMG_API_KEY')
    ])->post('https://app.html2img.com/api/html', [
        'html' => '<div style="padding: 20px; background: #f0f0f0;">
            <h1>Hello from Laravel!</h1>
            <p>Generated at ' . now() . '</p>
        </div>',
        'css' => 'h1 { color: #2563eb; } p { color: #4b5563; }',
        'width' => 800,
        'height' => 600,
    ]);

    if ($response->successful() && $response->json('success')) {
        return response()->json([
            'success' => true,
            'url' => $response->json('url'),
            'id' => $response->json('id'),
            'credits_remaining' => $response->json('credits_remaining'),
        ]);
    }

    return response()->json([
        'success' => false,
        'message' => $response->json('message', 'Unknown error'),
    ], $response->status());
}

Screenshot Example

Taking a screenshot of a webpage is just as simple:

use Illuminate\Support\Facades\Http;

public function takeScreenshot()
{
    $response = Http::withHeaders([
        'X-API-Key' => env('HTML2IMG_API_KEY')
    ])->post('https://app.html2img.com/api/screenshot', [
        'url' => 'https://example.com',
        'width' => 1200,
        'height' => 800,
        'dpi' => 2,
        'fullpage' => true,
    ]);

    if ($response->successful() && $response->json('success')) {
        return response()->json([
            'success' => true,
            'url' => $response->json('url'),
        ]);
    }

    return response()->json([
        'success' => false,
        'message' => $response->json('message', 'Unknown error'),
    ], $response->status());
}

Using with Blade Views

You can also generate images from Blade views:

public function generateFromView()
{
    $html = view('emails.invoice', [
        'order' => Order::find(1),
    ])->render();

    $response = Http::withHeaders([
        'X-API-Key' => env('HTML2IMG_API_KEY')
    ])->post('https://app.html2img.com/api/html', [
        'html' => $html,
        'width' => 800,
        'height' => 600,
    ]);

    if ($response->successful() && $response->json('success')) {
        return response()->json([
            'url' => $response->json('url'),
        ]);
    }

    return response()->json([
        'error' => $response->json('message', 'Unknown error'),
    ], $response->status());
}

Saving a Local Copy

If you’d rather persist the image on your own storage, download it from the returned URL:

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;

public function generateAndStore()
{
    $response = Http::withHeaders([
        'X-API-Key' => env('HTML2IMG_API_KEY')
    ])->post('https://app.html2img.com/api/html', [
        'html' => '<h1>Hello from Laravel!</h1>',
        'width' => 800,
        'height' => 600,
    ]);

    if (! $response->successful() || ! $response->json('success')) {
        abort(500, $response->json('message', 'Unknown error'));
    }

    $imageUrl = $response->json('url');
    $binary = Http::get($imageUrl)->body();

    Storage::disk('public')->put('images/output.png', $binary);

    return response()->json([
        'remote_url' => $imageUrl,
        'local_url' => Storage::url('images/output.png'),
    ]);
}

Run php artisan storage:link to make the storage directory publicly accessible when saving local copies.

Common patterns

Queueable job

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
use App\Models\Invoice;

class RenderInvoiceImage implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(public Invoice $invoice) {}

    public function handle(): void
    {
        $response = Http::withHeaders(['X-API-Key' => config('services.html2img.key')])
            ->post('https://app.html2img.com/api/v1/templates/invoice-image', [
                'invoice_number' => $this->invoice->number,
                'business_name' => $this->invoice->business->name,
                'client_name' => $this->invoice->client->name,
                'items' => $this->invoice->items->map->toApiArray()->all(),
                'total' => $this->invoice->total_formatted,
            ]);

        $this->invoice->update(['image_url' => $response->json('url')]);
    }
}

// Dispatch from a controller after invoice creation
RenderInvoiceImage::dispatch($invoice);

The job retries on failure based on your queue config and stores the returned URL on the model.

Templates in Laravel

Skip the HTML step with a named template. Here is the Invoice Image template:

$response = Http::withHeaders(['X-API-Key' => config('services.html2img.key')])
    ->post('https://app.html2img.com/api/v1/templates/invoice-image', [
        'invoice_number' => 'INV-2026-0042',
        'business_name' => 'Coastline Coffee Co',
        'client_name' => 'Riverside Bakery',
        'items' => [
            ['description' => 'Wholesale beans', 'quantity' => '50', 'unit_price' => '$15.00', 'amount' => '$750.00'],
        ],
        'total' => '$750.00',
    ]);

$url = $response->json('url');

The returned url is hosted on i.html2img.com.