Introducing sidecar-browsershot

• 3 min read

Earlier this year I've released a new Laravel package. It's called sidecar-browsershot. Here's what it does, how it works under the hood and for what you could use it.

As the name suggests, the package combines Sidecar for Laravel – a package to deploy and execute AWS Lambdas in Laravel – with Browsershot – a package to control Puppeteer with PHP.

To use Browsershot, you usually need Node and Google Chrome installed on your server. This can be a problem if you don't have control over the installed software on the server or you don't have enough resources to run Chrome on your server (booting and running a browser is a CPU intensive task).
sidecar-browsershot steps in here and removes the need for both Node and Chrome. Everything runs on AWS Lambda. 🀯

After you've installed the package and set up Sidecar, all you need to do is replace calls to Browsershot with BrowsershotLambda. Here is a code example to illustrate the code changes required to use sidecar-browsershot.

use Spatie\Browsershot\Browsershot;
use Wnx\SidecarBrowsershot\BrowsershotLambda;

// Run Chrome on your server and create a screenshot.
Browsershot::url('https://example.com')
    ->save('screenshot.jpg');

// Run Chrome on AWS Lambda and create a screenshot.
BrowsershotLambda::url('https://example.com')
    ->save('screenshot.jpg');

Neat, right?

But how does it work?

The package contains a BrowsershotLambda-class which extends the Browsershot-class from the original Spatie package.

BrowsershotLambda overwrites the callBrowser-method and, as the name would suggest, changes how the browser is called.
Instead of running Puppeteer and Chrome locally, the call is delegated to the BrowsershotFunction-class, which in turn, runs Google Chrome on AWS Lambda.

The BrowsershotFunction from the package sits on the shoulder of giants and combines the JavaScript code from Spatie, chrome-aws-lambda-layer and our own bit of JavaScript to make everything work nicely together.

(chrome-aws-lambda-layer is an important piece in the puzzle. This AWS Lambda layer is a Brotli compressed version of Google Chrome that barely fits into the limit of AWS. Only thanks to this layer sidecar-browsershot works.)

Is this ready for production use?

Yes. Yes it is! I've been using it in 3.screeenly.com since September 2021 and didn't run into any issues.

Only in rare cases the AWS Lambda request times out, but screeenly then falls back to a local Chrome installation, to complete the API request. (The issue here was that the website users tried to take screenshots of took more than a minute to respond.)

As of writing this post, the package was downloaded more than 16000 times.
In my eyes, this should be validation enough that this package could help you solve a problem you encounter in one of your projects. Give it a go.
The best thing: AWS offers a generous free tier for Lambda. If you don't execute thousands of Lambdas per month, you don't pay anything to run sidecar-browsershot.

But why exactly would I need Browsershot anyway?

You might ask yourself: Why would I need this package? Why would I ever need access to a Google Chrome instance in my app?

Did you ever need to create a PDF for an invoice with PHP? Was it a frustrating experience as the CSS you wrote did not properly render by the package you used?
Browsershot and Puppeteer can help here. Write the HTML and CSS in the framework you're already familiar with and pass it to Browsershot like so:

$htmlForInvoice = <<<HTML
    <div class="flex items-center">
        <div>
            Website Design
        </div>
        <div>
            $1000
        </div>
    </div>
    <!-- Obviously more HTML needed … -->
HTML;

BrowsershotLambda::html($htmlForInvoice)->pdf('invoice.pdf');

Your HTML, CSS and JavaScript is then rendered in a real browser and stored as a PDF.

Or do you want to generate open graph images for your blog or website? Generate the design of your images in your favourite CSS framework and pass the HTML to Browsershot.

$htmlForOpenGraphImage = <<<HTML
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" type="text/css" href="https://rsms.me/inter/inter.css">

    <style>
    #container {
      font-family: 'Inter';
      width: 1200px;
      height: 600px;
    }
    </style>

    <section id="container" class="flex flex-col justify-between p-12 bg-gray-900">
      <h2 class="text-2xl font-bold leading-10 tracking-tight text-gray-100">
        example.com
      </h2>
      <h1 class="text-6xl font-extrabold tracking-tight text-gray-100">
        The Title of your Article
      </h1>
      <footer class="text-gray-300 text-xl font-medium">
        5 min read
      </footer>
    </section>
HTML;

BrowsershotLambda::html($htmlForOpenGraphImage)
    ->windowSize(1200, 600)
    ->screenshot('open-graph-image.jpg');

This is just the tip of the iceberg. I'm sure once you start tinkering with Browsershot and Puppeteer many more use cases will come to mind.

Acknowledgments

Shout out to Aaron Francis for building Sidecar and helping me out to make sidecar-browsershot a reality.
Thanks to Spatie for creating the Browsershot package in the first place.
And big thanks to Adrian Brown for sharing his aproach on running Browsershot on AWS Lambda. His insights played a vital role in developing this package.

If you want to learn more about sidecar and how to use sidecar-browsershot check out Aaron's Laracon talk from February 2022.

Webmentions