Aug 27, 2026 · 6 min · Dev Guides

Using Claude Code: The unreasonable effectiveness of HTML

Using Claude Code: The unreasonable effectiveness of HTML

A 900-line terminal transcript is technically complete and practically unreadable. The same debugging session rendered as a 70 KB HTML file—with a timeline, grouped failures, request/response diffs, and collapsible stack traces—can turn a 30-minute review into a five-minute decision.

That is the unreasonable effectiveness of HTML in Claude Code: not using HTML merely to build websites, but using it as a universal interface for development work.

HTML gives an agent a cheap, expressive output surface. It supports hierarchy, tables, color, diagrams, forms, links, responsive layouts, and progressive disclosure without requiring a frontend project. Claude Code can generate one self-contained file, you can open it locally, and the artifact remains useful after the conversation disappears.

Why HTML works so well as an agent output format

Most agent output defaults to one of three forms:

A standalone HTML document sits between documentation and software. It has enough structure to communicate complex results, but almost no operational overhead.

Output formatBest useMain limitationSetup required
Terminal textShort answers, commands, status updatesPoor navigation and visual densityNone
MarkdownPlans, documentation, code review notesLimited interaction and layoutMarkdown viewer
JSONMachine-to-machine exchangeAwkward for humansParser or viewer
Standalone HTMLReports, prototypes, comparisons, investigationsHarder to diff; must be handled safelyBrowser
Full web appPersistent tools and production workflowsBuild and maintenance costRuntime, dependencies, deployment

In practice, HTML becomes valuable when the answer has more than one level of detail. A summary can remain visible while logs, SQL plans, or payloads live inside <details> elements. A table can show the result, while JavaScript filters it without another model request.

This is not about making everything colorful. It is about increasing the amount of information a developer can inspect without increasing cognitive load at the same rate.

Start with a constrained HTML artifact

A vague prompt such as “make a dashboard” tends to produce decorative UI with invented metrics. I give Claude Code a contract that specifies the evidence, interaction, and operating constraints.

For example:

Analyze the test results in artifacts/test-results.json.

Create artifacts/test-report.html with:
- one self-contained HTML file
- no CDN assets, network requests, frameworks, or build step
- a summary showing passed, failed, skipped, and total duration
- failures grouped by package
- searchable test names
- collapsible stdout and stack traces
- links to source files using relative paths when available
- a neutral layout that works from 360px to 1440px
- semantic HTML and keyboard-accessible controls

Do not invent missing values. Label unavailable data as "not recorded".
Escape all test output before placing it in the document.
After writing the file, verify that every count matches the source JSON.

The last three instructions matter more than the visual design. Agent-generated reports often fail through plausible fabrication, unsafe interpolation, or inconsistent totals—not invalid CSS.

I also ask Claude Code to separate facts from interpretation. For an incident report, that might mean three explicit sections:

  1. Observed: values directly present in logs.
  2. Derived: calculations such as elapsed time or failure rate.
  3. Hypotheses: possible explanations requiring confirmation.

HTML makes those categories visually distinct, but the underlying epistemic boundary still needs to be in the prompt.

A useful pattern: data first, presentation second

For repeatable workflows, do not let the model bury all extracted facts inside markup. Have it produce structured data, validate that data, and then render HTML.

A compact contract could look like this:

{
  "schema_version": 1,
  "summary": {
    "total": 142,
    "passed": 137,
    "failed": 3,
    "skipped": 2,
    "duration_ms": 48120
  },
  "failures": [
    {
      "name": "test_refreshes_expired_token",
      "file": "tests/auth/test_refresh.py",
      "line": 84,
      "message": "expected 200, received 401"
    }
  ]
}

Validate invariants before rendering:

import json
from pathlib import Path

report = json.loads(Path("artifacts/report.json").read_text())

summary = report["summary"]
assert summary["total"] == (
    summary["passed"] + summary["failed"] + summary["skipped"]
)
assert summary["duration_ms"] >= 0

for failure in report["failures"]:
    assert failure["file"]
    assert isinstance(failure["line"], int)

Then generate the document with ordinary code. If values can contain user-controlled or tool-generated text, escape them:

from html import escape

def failure_row(item: dict) -> str:
    name = escape(item["name"])
    location = escape(f'{item["file"]}:{item["line"]}')
    message = escape(item["message"])

    return f"""
      <tr>
        <td><code>{name}</code></td>
        <td><code>{location}</code></td>
        <td>{message}</td>
      </tr>
    """

A common gotcha is assuming that “local HTML” means “safe HTML.” Build logs can contain branch names, commit messages, API payloads, and other untrusted strings. Injecting those strings through innerHTML creates the same cross-site scripting problems found in a hosted application. Prefer escaped server-side rendering or DOM APIs such as textContent.

Make the artifact inspectable, not merely attractive

The most effective agent-generated HTML I use has a few consistent properties.

Progressive disclosure

Put the decision-relevant result first, then allow expansion:

<details>
  <summary>Request payload and response headers</summary>
  <h3>Request</h3>
  <pre><code id="request-body"></code></pre>
  <h3>Response headers</h3>
  <pre><code id="response-headers"></code></pre>
</details>

Long stack traces, raw model responses, query plans, and environment details should not dominate the initial view.

Evidence attached to claims

If a card says “12 endpoints changed,” clicking it should reveal those 12 endpoints. If a performance report says one route is slower, show the compared durations and calculation. Avoid unexplained status badges.

Explicit empty and uncertain states

An empty array may mean “nothing failed,” “collection failed,” or “the field was absent.” Those are different states. Render them differently instead of defaulting all three to zero.

Local, reproducible assets

For disposable reports, inline CSS and small scripts are a good trade-off. External fonts, analytics, CDN scripts, and remote images make the artifact slower, less private, and less reproducible.

For larger tools, however, a single file becomes a liability. Once the HTML contains thousands of lines of embedded JSON and JavaScript, move to a small generated site or a real application with tests.

Verify HTML through a browser loop

Claude Code can inspect source files, but valid source does not guarantee a usable page. Layout overflow, inaccessible controls, and JavaScript errors only become obvious at runtime.

If the project already uses Playwright, ask Claude Code to perform a browser smoke test rather than installing a new stack unnecessarily:

python -m http.server 8000 --directory artifacts

Then test the artifact:

import { test, expect } from "@playwright/test";

test("test report exposes failures", async ({ page }) => {
  await page.goto("http://127.0.0.1:8000/test-report.html");

  await expect(page.getByRole("heading", { name: "Test report" }))
    .toBeVisible();
  await expect(page.getByText("3 failed")).toBeVisible();

  await page.getByRole("textbox", { name: "Filter tests" })
    .fill("refresh");
  await expect(page.getByText("test_refreshes_expired_token"))
    .toBeVisible();
});

What actually happens when this loop is skipped is predictable: the file looks convincing in source form, but a filter silently fails because an element ID changed, or the summary grid clips on a laptop-sized viewport.

At minimum, verify:

The same technique works with Claude, GPT, and Gemini APIs

HTML is an output pattern, not a Claude-specific capability. Claude Code makes it convenient because it can inspect a repository, write the artifact, and run local checks. In an API pipeline, I use the model for analysis and structured extraction, then use deterministic application code for validation and rendering.

That division reduces token usage and makes failures easier to diagnose:

logs + schema -> model -> validated JSON -> renderer -> report.html

Do not repeatedly send CSS boilerplate and a large historical report back to the model. Send the relevant source data and request a compact JSON result. Cache stable context, paginate large inputs, and read the actual usage fields returned by the API rather than estimating from character count.

The cost equation is straightforward:

request cost =
  (input_tokens / 1,000,000 × input_price)
  + (output_tokens / 1,000,000 × output_price)

For example, if a workflow sends 80,000 input tokens and receives 6,000 output tokens, it consumes 0.08 million input tokens and 0.006 million output tokens per run. Multiply those quantities by the current rates for the selected model. Keeping a 25,000-token HTML template out of every request saves exactly 25,000 input tokens per run, regardless of provider.

Model choice should follow the task. Use a stronger reasoning model for ambiguous incident analysis or architecture comparison; use a faster, cheaper model for schema-constrained classification or summarization. AI Prime Tech can be useful when you want cheaper multi-model API access and need to compare Claude, GPT, and Gemini on the same report pipeline rather than coupling the workflow to one provider.

Where HTML is the wrong choice

HTML is less effective when the output must be consumed primarily by another program, reviewed line by line in Git, or maintained as a long-lived product.

There are also real limits:

Treat the HTML as a view, not the source of truth. Keep the underlying JSON, commands, and generation instructions beside it. For sensitive reports, avoid embedding secrets and consider disabling scripts entirely. If an artifact must render untrusted HTML, isolate it with a restrictive sandbox and Content Security Policy rather than trusting the model to sanitize it perfectly.

Practical takeaways

HTML is effective here because it is the browser’s native document format, not because an agent turns it into magic. Used deliberately, it gives coding agents a human-scale output channel: richer than a terminal, cheaper than an application, and concrete enough to verify.

MR
Marcus Reed · Senior API Engineer

Marcus has spent 9 years building LLM-backed products and integrating the Claude, GPT and Gemini APIs into production systems. He writes about API cost optimization, agent architecture, and practical model selection.

Get cheaper Claude API access

One API key for Claude Opus 4.8, Sonnet 4.6, Haiku 4.5, Fable 5, plus GPT & Gemini — up to 80% off official pricing, pay-as-you-go.

Get Your API Key →
AI Prime Tech is an independent third-party API gateway. Claude™ and Anthropic® are trademarks of Anthropic, PBC. No affiliation or endorsement is implied.