PHPackages                             jmoo/chrome-react - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. jmoo/chrome-react

ActiveLibrary[HTTP &amp; Networking](/categories/http)

jmoo/chrome-react
=================

Async, low-level client for the Chrome DevTools Protocol using ReactPHP

136052[1 PRs](https://github.com/jmoo/chrome-reactphp/pulls)PHP

Since Dec 9Pushed 3y ago1 watchersCompare

[ Source](https://github.com/jmoo/chrome-reactphp)[ Packagist](https://packagist.org/packages/jmoo/chrome-react)[ RSS](/packages/jmoo-chrome-react/feed)WikiDiscussions master Synced 2d ago

READMEChangelogDependenciesVersions (2)Used By (0)

jmoo/chrome-react
=================

[](#jmoochrome-react)

[![Build Status](https://camo.githubusercontent.com/488a746f021a7e115c692414d65a76c3aec033ddb62a09f8a8fb1f0475143d93/68747470733a2f2f7472617669732d63692e636f6d2f6a6d6f6f2f6368726f6d652d72656163747068702e7376673f6272616e63683d6d6173746572)](https://travis-ci.com/jmoo/chrome-reactphp)

[DevTools Api](https://chromedevtools.github.io/devtools-protocol/tot)

Fully async, low-level client for the Chrome DevTools Protocol using ReactPHP

*Warning: Experimental! Expect large breaking changes, instability, and lack of documentation until there is a tagged version*

### Installation

[](#installation)

```
$ composer require jmoo/chrome-react
```

### Getting Started

[](#getting-started)

#### Running Chrome

[](#running-chrome)

```
$ chrome --headless --disable-gpu --remote-debugging-port=9222
```

#### Navigate to a page (synchronously)

[](#navigate-to-a-page-synchronously)

The blocking client runs the event loop while awaiting completion of each task. This allows you to write normal synchronous code while still responding to asynchronous events.

```
$chrome = new \Jmoo\React\Chrome\Blocking\Client;
$url = $chrome->new()->webSocketDebuggerUrl;
$tab = $chrome->connect($url);

$tab->Page->enable();
$tab->Page->navigate(['url' => 'https://www.chromium.org/']);
$tab->disconnect();
```

#### Navigate to a page (asynchronously)

[](#navigate-to-a-page-asynchronously)

The async client returns Promises for each command.

```
$loop = \React\EventLoop\Factory::create();
$chrome = new \Jmoo\React\Chrome\Async\Client($loop);

$chrome
    ->new()
    ->then(function ($page) use ($chrome) {
        return $chrome->connect($page->webSocketDebuggerUrl);
    })
    ->then(function ($c) {
        return \React\Promise\all([
            $c,
            $c->Page->enable(),
            $c->Page->navigate(['url' => 'https://www.google.com'])
        ]);
    })
    ->then(function ($result) {
        list($c) = $result;
        $c->disconnect();
    });

$loop->run();
```

#### Navigate to a page (coroutines)

[](#navigate-to-a-page-coroutines)

The async client can be used with amphp coroutines using `amphp/react-adapter`

```
\Amp\Loop::run(function() {
    $chrome = new \Jmoo\React\Chrome\Async\Client(ReactAdapter::get());

    $tabInfo = yield $chrome->new();
    $tab = yield $chrome->connect($tabInfo->webSocketDebuggerUrl);

    yield $tab->Page->enable();
    yield $tab->Page->navigate(['url' => 'https://news.ycombinator.com/']);
    $tab->disconnect();
});
```

#### Using an existing event loop with the blocking client

[](#using-an-existing-event-loop-with-the-blocking-client)

```
// any existing event loop
$loop = \React\EventLoop\Factory::create();

// create a new async client with event loop
$async = new \Jmoo\React\Chrome\Async\Client($loop);

// create a new blocking client using async client
$chrome = new \Jmoo\React\Chrome\Blocking\Client($async);
```

### Usage

[](#usage)

#### Configuration

[](#configuration)

```
# Default options
$client = (new Client)->withOptions([
    'host' => '127.0.0.1',
    'port' => 9222,
    'ssl' => false,
    'timeout' => 30 // blocking client only
]);

# Using a custom event-loop and connector
$asyncClient = new \Jmoo\React\Chrome\Async\Client($loop, $connector);
$blockingClient = new \Jmoo\React\Chrome\Blocking\Client($asyncClient);
```

#### Domains

[](#domains)

```
$client = new \Jmoo\React\Chrome\Blocking\Client;
$c = $client->connect($client->new()->webSocketDebuggerUrl);

// getting a domain accessor
$page = $c->Page;   // with magic method
$page = $c->getDomain('Page'); // directly

// enable events and retrieve multiple domain accessors at the same time
list($page, $network, $log) = $c->enable(['Page', 'Network', 'Log']);
```

#### Methods

[](#methods)

```
$client = new \Jmoo\React\Chrome\Blocking\Client;
$c = $client->connect($client->new()->webSocketDebuggerUrl);

// executing a method using the domain accessor
$c->Page->navigate(['url' => 'http://jmoo.io']); // with magic method
$c->Page->send('navigate', ['url' => 'http://jmoo.io']); // directly

// without using domain accessor
$c->send('Page.navigate', ['url' => 'http://jmoo.io']);
```

#### Events

[](#events)

```
$client = new \Jmoo\React\Chrome\Blocking\Client;
$c = $client->connect($client->new()->webSocketDebuggerUrl);

// events must be enabled
$c->Page->enable();

$c->Page->on('domContentEventFired', function() use ($c) {
    $c->disconnect();
});

// pause execution until disconnect (blocking client only)
$c->waitForDisconnect();
```

#### Sessions

[](#sessions)

```
$client = new \Jmoo\React\Chrome\Blocking\Client;
$c = $client->connect($client->version()->webSocketDebuggerUrl);

$target = $c->send('Target.createTarget', ['url' => 'about:blank']);
$session = $c->createSession($target->targetId);
$session->Page->enable();
```

###  Health Score

25

—

LowBetter than 35% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity21

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 93.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/8016155?v=4)[John Moore](/maintainers/jmoo)[@jmoo](https://github.com/jmoo)

---

Top Contributors

[![jmoo](https://avatars.githubusercontent.com/u/8016155?v=4)](https://github.com/jmoo "jmoo (15 commits)")[![WyriHaximus](https://avatars.githubusercontent.com/u/147145?v=4)](https://github.com/WyriHaximus "WyriHaximus (1 commits)")

### Embed Badge

![Health badge](/badges/jmoo-chrome-react/health.svg)

```
[![Health](https://phpackages.com/badges/jmoo-chrome-react/health.svg)](https://phpackages.com/packages/jmoo-chrome-react)
```

###  Alternatives

[php-http/cache-plugin

PSR-6 Cache plugin for HTTPlug

25126.1M81](/packages/php-http-cache-plugin)[illuminate/http

The Illuminate Http package.

11937.9M6.8k](/packages/illuminate-http)[rdkafka/rdkafka

A PHP extension for Kafka

2.2k24.3k1](/packages/rdkafka-rdkafka)[httpsoft/http-message

Strict and fast implementation of PSR-7 and PSR-17

87965.9k114](/packages/httpsoft-http-message)[mezzio/mezzio-router

Router subcomponent for Mezzio

265.4M89](/packages/mezzio-mezzio-router)[serpapi/google-search-results-php

Get Google, Bing, Baidu, Ebay, Yahoo, Yandex, Home depot, Naver, Apple, Duckduckgo, Youtube search results via SerpApi.com

69127.2k](/packages/serpapi-google-search-results-php)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
