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

Html Faker Laravel Package

awcodes/html-faker

A Laravel package that adds HTML-aware fake data generators to Faker, letting you seed tests and demos with realistic markup—headings, paragraphs, lists, links, and more—instead of hand-written placeholder HTML.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the matrix before anything else. This package is archived at v0.4.0 (2025-06-10). What it declares in composer.json — PHP constraint, illuminate/support constraint, Faker major version — is the permanent ceiling. Run this against your actual lockfile rather than trusting the release note:

composer why-not awcodes/html-faker ^0.4

v0.4.0 is described only as "add support for Laravel 12." That reads as a constraint widening, but a one-line release note on an archived repo is not a compatibility guarantee. If you are on Laravel 11, verify 12 support was added alongside rather than swapped in.

Install as a dev dependency.

composer --dev require awcodes/html-faker:^0.4

Service provider auto-discovery handles registration; there is no documented publish step. If you find yourself looking for vendor:publish, stop — there is almost certainly nothing to publish.

Read in this order:

  1. The README usage block. It is the only authoritative statement of the public entry point (Faker provider method vs. facade vs. app() binding). Treat it as unverified until you have executed it once — README and code are the same age here.
  2. src/. The whole package is a FakerPHP provider, a few hundred lines. Ten minutes. You will likely end up owning this logic (see Gotchas), so the read is not wasted.
  3. composer.json, for the constraints above.

First use case: one factory state, not a seeder. Pick a model with a body (longText) column — Article, Post, CMS Block. Put the package behind a wrapper immediately:

// tests/Support/HtmlFixture.php  (verify the real namespace from src/)
namespace Tests\Support;

use Awcodes\HtmlFaker\...; // confirm entry point from README + src/

class HtmlFixture
{
    /** Raw, unsanitized HTML. Test/demo data only. */
    public function blocks(int $count = 4): string { /* delegate */ }
}

Then a single state:

public function withHtmlBody(): static
{
    return $this->state(fn () => ['body' => app(HtmlFixture::class)->blocks()]);
}

Run it once on a branch, dd() the output, and confirm you got structured markup (headings, paragraphs, lists, links) rather than escaped or truncated text. That check is the entire spike — everything else is variation.

Implementation Patterns

Single-seam wrapper. Never call the package from more than one place. One class in tests/Support (or app/Support if seeders live in the app) exposing blocks(), paragraph(), inline(). This is the pattern that matters: when the pin inevitably fails against a future PHP or Laravel major, you replace the guts of one file and nothing in the codebase moves. Half a day of work that buys the entire migration.

Seeder integration: chunk, don't row-loop. HTML generation is not the bottleneck in a bulk seeder; per-row inserts are.

Article::insert(
    Article::factory()->count(200)->withHtmlBody()->raw()
);

Keep samples modest — a handful of blocks per row. Deeply nested markup across hundreds of rows is measurable generation time and invisible in a local database.

Test assertions: pick one style per suite. Structure assertions are the durable choice:

// Style A — structure, no seeding
$response->assertSee('<h2', escape: false);
expect($article->body)->toContain('<ul>');

// Style B — deterministic, accept the collateral
fake()->seed(1234);
$html = app(HtmlFixture::class)->blocks();

Style B has a non-obvious cost: fake() is a shared resolver, so seeding it also fixes every other Faker-generated attribute in that test process. If you need determinism, seed once in setUp and own the fact that the rest of the test data is now fixed too.

Preview routes for content-heavy surfaces. Highest-leverage usage: a local-only route that renders generated markup through your real Blade views and real sanitizer pipeline.

if (app()->isLocal()) {
    Route::get('/dev/html-preview', fn () => view('dev.html-preview', [
        'body' => app(HtmlFixture::class)->blocks(8),
    ]));
}

This is how you discover your prose styles break on nested lists or long links before someone files a "layout bug" that is actually fixture output.

Profile-based generation. Generic blocks are generic. Where you need email bodies, product cards, or testimonial modules, compose inside the wrapper rather than forking the package:

public function profile(string $type): string
{
    return match ($type) {
        'email'   => $this->paragraph() . '<hr>' . $this->inline(),
        'article' => $this->blocks(6),
        default   => $this->paragraph(),
    };
}

Because you own the seam, a bespoke block is twenty lines and touches no dependency.

If you register it as a first-class Faker formatter, do it in a dev-only code path. Registering a fixture provider in AppServiceProvider that boots in production is how a test utility becomes a production dependency.

Gotchas and Tips

Archived means permanent ownership. Plan the exit on day one. No Laravel 13, no PHP 8.5, no security triage, no issue responses. Pin the version, flag it in your dependency inventory as archived, exclude it from Dependabot/Renovate, and name an owner. Silent automated bumps on archived packages are how teams end up debugging phantom test failures.

Never assert on exact generated strings. Highest-probability day-one failure by a wide margin, and it presents as a flaky test rather than an obvious misuse. Assert on structure or seed. Do not snapshot output into a fixture file — you have just reintroduced the hand-maintained fixture the package was supposed to eliminate.

It generates strings; it does not validate or sanitize them. No DOM-awareness, no well-formedness guarantee, no accessibility checking. Two hard rules: never let generated HTML reach a user-facing view outside local/staging preview, and always push it through your real sanitizer in any test claiming to cover rendering. Also stop anyone using this as a sanitizer test corpus in review — it produces plausible markup, not adversarial markup, which is precisely the wrong input for that test.

require-dev has one carve-out. If a seeder referencing the wrapper runs during a production or staging deploy, --dev will break that deploy. Check your deploy process before committing. Safe default: require-dev plus a written rule that production seeders must not depend on HTML-faker output.

Debug malformed markup by dumping the raw string, not the view. dd() before Blade. Nine times out of ten an unclosed tag or empty heading is generator output, not CSS or a browser bug. Five-second check, saves an hour.

{!! !!} belongs only in the preview view. The bypass is the point of that route, but it must be gated at the route level — app()->isLocal() — not merely visually marked. Don't let a copy of that Blade file drift into a real page.

Faker seeding leaks across the suite. fake()->seed() is global state. One test reseeding for deterministic HTML makes every subsequent Faker call in the same process deterministic, including tests you never touched. Prefer structure assertions.

Column types. Use text/longText. Resist storing generated markup as JSON for "structured fixtures" — you get the worst of both worlds and lose direct-render ability.

The extension point is your wrapper, not the package. No config to publish, no plugin registry worth relying on, pre-1.0 with no upstream stewardship. Need different output? Subclass or reimplement inside HtmlFixture. That keeps the blast radius at one file when the pin eventually breaks — and given the archive status, budget 1–2 engineer-days, once, for the day you copy the provider into your own support code with attribution.

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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core