Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Amp Toolbox Laravel Package

ampproject/amp-toolbox

AMP Toolbox is a PHP library from the AMP Project for building and optimizing AMP pages. It offers server-side rendering, DOM transformation, CSS handling, and an AMP optimizer that reduces page size and improves performance while helping ensure valid AMP HTML output. Used by the official AMP plugin...

View on GitHub
Deep Wiki
Context7

Getting Started

The package is the PHP port of the Node @ampproject/toolbox-optimizer. It is not a Laravel integration; it is a single-purpose library that takes a rendered AMP HTML document and returns an optimised one (server-side renders layouts, strips the AMP boilerplate, inlines critical CSS, and reorders <head>).

Minimal setup:

composer require ampproject/amp-toolbox

Requirements to check before writing any code: PHP ≥ 7.2 and ext-dom/ext-libxml. Both are almost always present in a Laravel install, but confirm with php -m | grep -E 'dom|libxml'.

Where to look first, in this order:

  1. src/Optimizer/AmpTransformer.php — the only class you will call in practice. It is the transformer chain and the public API.
  2. src/Transformer/ — the individual transformers (boilerplate, runtime CSS, server-side rendering, head reordering). This is where the behaviour lives if the output is not what you expect.
  3. src/RuntimeVersion.php — the helper that fetches the current AMP runtime version, used only if you self-host v0.css.
  4. tests/ — full before/after HTML fixtures; they are the best documentation of what "optimised" means.

First use case — optimise a rendered AMP view before the response goes out:

use AmpProject\AmpToolbox\Optimizer\AmpTransformer;

$transformer = new AmpTransformer([
    'canonical' => 'https://example.com/posts/hello-world',
]);

$html = $transformer->transformHtml(view('posts.amp', $post)->render());

The two things to get right on day one are: (a) feed it a complete document, not a Blade partial, and (b) supply the correct canonical URL, because several transformers (boilerplate removal, hero preloading) depend on it.

Implementation Patterns

Middleware on AMP routes only. The single most common integration is a middleware that inspects the request/response for AMP (?amp, an /amp/ prefix, or a text/html response for an AMP view) and optimises only those responses. Do not run it globally — it is a DOM parse plus CSS rewrite per request and will noticeably slow non-AMP endpoints.

class OptimizeAmpResponse
{
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);

        if (! $request->is('amp/*') || ! $response instanceof \Illuminate\Http\Response) {
            return $response;
        }

        $canonical = $request->attributes->get('amp.canonical'); // set by the controller
        if (! $canonical) {
            return $response;
        }

        $transformer = new AmpTransformer(['canonical' => $canonical]);
        $response->setContent($transformer->transformHtml($response->getContent()));

        return $response;
    }
}

Per-request construction, not a singleton. AmpTransformer is constructed with options that include the canonical URL, which is inherently per-page. Bind a small factory rather than a singleton in the container:

$this->app->bind(AmpOptimizer::class, fn () => new AmpOptimizer());
// where AmpOptimizer::optimize(string $html, string $canonical): string wraps AmpTransformer

Cache the result. The transformation is deterministic given (HTML, canonical, runtime version). Cache the final HTML in Laravel's cache keyed by route + updated_at + runtime version; the transformer is expensive enough that a first-paint improvement is offset by slow TTFB on uncached pages.

$html = Cache::remember(
    "amp:post:{$post->id}:{$post->updated_at->timestamp}:{$runtimeVersion}",
    now()->addHours(6),
    fn () => $optimizer->optimize(view('posts.amp', $post)->render(), $post->ampUrl())
);

Self-host the AMP runtime. The optimiser can reference a locally served v0.css instead of cdn.ampproject.org. Use RuntimeVersion in a scheduled command to fetch the current version, download https://cdn.ampproject.org/v0.css, store it under public/amp/v0-{version}.css, and pass that version into the transformer options so the transformed HTML points at your file. This removes a third-party DNS/TLS lookup from the critical path and is the single biggest win.

Pest/PHPUnit assertions. The transformer adds an attribute (historically transformed) on <html> and removes <style amp-boilerplate>. Assert on those two markers rather than exact HTML; the markup around amp-img is intentionally unstable across runtime versions.

$html = $optimizer->optimize($fixtureHtml, 'https://example.com/a');
expect($html)->toContain('transformed')
             ->not->toContain('amp-boilerplate');

Custom transformers. The chain is extensible — add a class that implements the package's transformer contract (see src/Transformer/TransformerAbstract.php for the exact base class) and register it in the AmpTransformer construction options. Useful for injecting analytics hints or adding data-hero to above-the-fold images the default transformer cannot infer from Blade markup.

Integration with Blade. Keep the AMP template in resources/views/amp/ and render it the same way you render a normal view. Do not try to build AMP HTML with a component library that emits fragments — the transformer requires one well-formed document.

Gotchas and Tips

The package is effectively frozen. Last release is 2021-05-31 and the 0.x version line never stabilised. Pin an exact version in composer.json and treat any update as a source-code review. On PHP 8.1+ expect deprecation notices from 2021-era DOM code (libxml_disable_entity_loader, implicit nullable types on transformer methods); either suppress them in a scoped error handler or patch the offending files.

Full documents only. DOMDocument::loadHTML will happily "fix" a partial view by injecting <html><body>, which silently corrupts the transform. Always call it on view()->render(), never on a section or a component string.

canonical is required. A handful of transformers no-op or misbehave without it. Derive it in the controller ($post->ampUrl()) and pass it via request attributes to the middleware — do not guess it from the request URL, because ?amp and /amp/ URLs will produce wrong canonicals and break SEO.

Network access is implicit. Without a pinned runtime version, the optimiser will try to fetch https://cdn.ampproject.org/rtv/.../v0.css. On an air-gapped CI, a queue worker, or a firewalled staging box this manifests as a multi-second hang, not a clean exception. Always pin the runtime version in non-production and warm the version in a scheduled command in production.

Not idempotent by design. Running it twice will re-run the server-side rendering and can double-wrap images or reinsert boilerplate. Guard with the transformed marker on <html> before calling.

amp-img becomes amp-img + img. Server-side rendering rewrites <amp-img> into an <amp-img> and an <img> pair with inline styles. Any JavaScript in your page that queries amp-img at DOM-ready time, or any test that asserts exact markup, will need adjusting. This is the point of the library, but it surprises people the first time.

Debugging a bad transform. Build the transformer with a minimal options array (just canonical) and diff the output against the input. If you need to isolate a transformer, instantiate it directly from src/Transformer/ — they are standalone and much easier to reason about than the composed stack. Compare against the fixture in tests/ with the same canonical; if the fixture is fine and yours is not, the input document is malformed.

Memory on large pages. CSS inlining plus a full DOM parse can push a 1 MB AMP page to tens of MB of PHP memory. For marketing pages with lots of inline critical CSS, either cache aggressively or raise memory_limit on the AMP route only.

Laravel-specific: do not put the transformer in php artisan serve during development if you are iterating on the AMP template — the transform will mask template bugs. Disable the middleware locally, fix the view, then re-enable to verify the optimiser's output.

Extension points are the escape hatch. If the optimiser is doing something you disagree with (e.g. removing a style tag you relied on), do not post-process the string with regex. Either disable the specific transformer via its option, or add a custom transformer after it in the chain — both are supported and both survive minor package updates, which regexing will not.

Reconsider whether you need it. If AMP is no longer a meaningful acquisition channel for the project, the transformer is pure complexity: a frozen dependency, DOM-rewriting behaviour, and a cache-key surface. Confirm the SEO/product case before adding it to a Laravel codebase in 2024+.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route