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.
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.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.
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.
filipegar/inky, but the class namespace is Hampe\Inky. Use use Hampe\Inky\Inky;. IDE autocomplete and package discovery may be confusing.inky in a build pipeline or wrapping/vendoring this parser behind your own interface so it can be replaced.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.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.tijsverkoyen/css-to-inline-styles. Add the email doctype, <meta name="viewport">, and Outlook-specific handling yourself.<columns> or <row> may be interpreted as email structure.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.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.new Inky(16).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.composer why-not filipegar/inky and test installation in a clean environment before committing to it.How can I help you explore Laravel packages today?