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

Inky Laravel Package

filipegar/inky

Inky is a PHP implementation of ZURB's Inky parser for Foundation for Email. It converts semantic components like button, callout, and row into table-based HTML emails, with configurable grid columns, tag aliases, and custom component factories.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and verify the real namespace before writing code—the package is filipegar/inky, but the README uses Hampe\Inky\Inky, so it is likely a fork/copy of another package.

composer require filipegar/inky

First places to look:

  • vendor/filipegar/inky/src/Inky.php — the public API: constructor, addAlias(), addComponentFactory(), releaseTheKraken().
  • vendor/filipegar/inky/src/Component/ — the supported Foundation for Email tags (container, row, columns, button, callout, etc.).
  • vendor/filipegar/inky/composer.json — PHP constraints and the php-html-parser dependency.
  • The README examples and any tests/fixtures. With 1 star and 0 dependents, your own tests are the real safety net.

Minimal Laravel use case: render a Blade email template containing Foundation for Email/Inky tags, compile it to table-based email HTML, then send the compiled HTML.

use Hampe\Inky\Inky;
use Illuminate\Support\Facades\Mail;

$html = view('emails.welcome', ['user' => $user])->render();

$compiled = (new Inky(12))->releaseTheKraken($html);

Mail::html($compiled, function ($message) use ($user) {
    $message->to($user->email)->subject('Welcome');
});

Example Blade view:

<container>
    <row>
        <columns small="12" large="6">
            Hello {{ $user->name }}
        </columns>
        <columns small="12" large="6">
            Thanks for joining.
        </columns>
    </row>
</container>

The first thing to verify is the output: it should contain normal <table> markup and no remaining <container>, <row>, or <columns> tags. Also confirm whether releaseTheKraken() returns the compiled string or echoes it in your version; assign the return value if possible, otherwise wrap it with output buffering.

Implementation Patterns

Wrap the parser in an application service. Avoid calling new Inky() all over the codebase. A small compiler class gives you one place for grid configuration, aliases, and custom components.

namespace App\Mail;

use Hampe\Inky\Inky;

final class InkyCompiler
{
    public function compile(string $html): string
    {
        $inky = new Inky(config('mail.inky.grid_columns', 12));

        // Optional: register project-wide aliases.
        $inky->addAlias('panel', 'callout');

        return $inky->releaseTheKraken($html);
    }
}

Bind it in AppServiceProvider if useful, but prefer a fresh Inky instance per compile unless your aliases/components are truly global and immutable.

$this->app->singleton(InkyCompiler::class);

Compile after Blade rendering, before sending. Inky must see final HTML, not Blade source. Render the view first, then compile, then hand the raw HTML to Laravel’s mailer.

$html = view('emails.receipt', compact('order'))->render();
$compiled = app(InkyCompiler::class)->compile($html);

Mail::html($compiled, function ($message) use ($order) {
    $message->to($order->customer->email)
        ->subject('Your receipt');
});

If you use Mailable, remember that Laravel will render the view as-is. A normal Mailable view containing <columns> will send those tags to the client unless you compile the rendered HTML yourself. For simple transactional mail, Mail::html() after compilation is the clearest path. For complex mailables, create a small base class or job that renders, compiles, and sends the final HTML.

Use jobs for queued email compilation. Compile inside the job’s handle() method so the job receives data, renders Blade, compiles Inky, and sends in one place.

public function handle(InkyCompiler $compiler): void
{
    $html = view('emails.welcome', ['user' => $this->user])->render();
    $compiled = $compiler->compile($html);

    Mail::html($compiled, function ($message) {
        $message->to($this->user->email)->subject('Welcome');
    });
}

Use aliases for readable templates. If your team prefers semantic names, map them to supported Inky components.

$inky = new Inky();
$inky->addAlias('test', 'callout');

$inky->releaseTheKraken('<test>Important notice</test>');

The README example is missing a semicolon after addAlias(...); fix that in your own code.

Add custom components only when necessary. Implement ComponentFactoryInterface, register it with addComponentFactory(), and follow the return type used by the built-in factories in src/Component/.

use Hampe\Inky\Component\ComponentFactoryInterface;
use Hampe\Inky\Inky;
use PHPHtmlParser\Dom\HtmlNode;

final class PromoCodeFactory implements ComponentFactoryInterface
{
    public function getName(): string
    {
        return 'promo-code';
    }

    public function parse(HtmlNode $element, Inky $inkyInstance)
    {
        // Inspect built-in factories first.
        // Mutate/replace $element and return whatever the built-ins return.
    }
}

Test the compiler directly. Mail::fake() will not prove that Inky transformed your markup. Add a small unit test with a representative template and assert that the output contains <table and does not contain <columns>.

$compiled = app(InkyCompiler::class)->compile(
    '<container><row><columns>Hello</columns></row></container>'
);

$this->assertStringContainsString('<table', $compiled);
$this->assertStringNotContainsString('<columns', $compiled);

Cache static shells if volume matters. For high-volume transactional email, precompile the static header/footer/layout shell and inject dynamic content with placeholders or Blade partials around the compiled shell. php-html-parser is a DOM parser, so avoid reparsing large static layouts on every send if profiling shows it matters.

Gotchas and Tips

  • Package/namespace mismatch. The Composer package is filipegar/inky, but the class namespace is Hampe\Inky. Use use Hampe\Inky\Inky;. IDE autocomplete and package discovery may be confusing.
  • Very low community support. One star, zero dependents, and README-level maturity means treat this as internal code. Pin an exact version, review the source, and keep an integration test suite. If you need long-term maintenance, consider ZURB’s Node inky in a build pipeline or wrapping/vendoring this parser behind your own interface so it can be replaced.
  • No Laravel service provider, facade, or config file. You instantiate and configure it manually. Put your own config in config/mail.php or a custom config/inky.php, but do not expect php artisan vendor:publish.
  • releaseTheKraken() is the whole API. Confirm in your installed version whether it returns the compiled HTML or echoes it. Wrap it in your own compile() method so the rest of the app never depends on that method name.
  • The parser normalizes HTML. php-html-parser may add <html>/<body>, reorder attributes, change quoting, or auto-close tags. Do not assert exact whitespace or attribute order. Validate structure and test in real email clients.
  • Inky only transforms structure; it does not inline CSS. You still need Foundation for Email CSS and a CSS inliner such as tijsverkoyen/css-to-inline-styles. Add the email doctype, <meta name="viewport">, and Outlook-specific handling yourself.
  • Blade first, Inky second. Never pass Blade source to Inky. Also escape user-provided content before compilation; otherwise user text containing <columns> or <row> may be interpreted as email structure.
  • Aliases can leak on shared instances. If you bind Inky as a singleton and call addAlias() per request, aliases persist across requests. Register global aliases once in a service provider, or use a fresh instance per compile.
  • Custom factory signatures matter. The README’s parse() example omits a return statement. Check the installed built-in factories and match their return type exactly. Write a test for every custom tag.
  • Grid columns are constructor-only. Default is 12. If your Foundation grid uses a different column count, pass it explicitly: new Inky(16).
  • Debug by dumping compiled HTML. Write the output to storage/app/inky-debug.html or dd() it in Tinker. Compare against ZURB Inky’s expected table markup for the same template. Keep fixtures for every supported component you use.
  • Check PHP/Laravel compatibility early. With a low-maturity package, the dependency constraints may lag PHP 8.x or Laravel 10/11. Run composer why-not filipegar/inky and test installation in a clean environment before committing to it.
  • Unsupported tags silently pass through. If a Foundation for Email tag is not implemented, it may remain in the output or be normalized oddly. Maintain a list of tags your templates use and assert their compiled output in tests.
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.
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log