Weave Code
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

Laravel Markdown Response Laravel Package

spatie/laravel-markdown-response

Serve markdown versions of your Laravel HTML pages for AI agents and bots. Detect markdown requests via Accept: text/markdown, known user agents, or .md URLs. Driver-based conversion (local PHP or Cloudflare Workers AI), with caching and HTML preprocessing.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/laravel-markdown-response
    

    Publish the config (optional):

    php artisan vendor:publish --provider="Spatie\MarkdownResponse\MarkdownResponseServiceProvider"
    
  2. First Use Case: Convert a route response to markdown by adding the MarkdownResponse middleware to your route or controller:

    Route::get('/docs', function () {
        return view('documentation');
    })->middleware(\Spatie\MarkdownResponse\Middleware\MarkdownResponse::class);
    
    • Access the endpoint with a Accept: text/markdown header (e.g., via curl -H "Accept: text/markdown" http://your-app.test/docs).
  3. Where to Look First:

    • README for basic usage.
    • config/markdown-response.php for customization (e.g., allowed routes, parsers).
    • app/Http/Middleware/ for middleware configuration.

Implementation Patterns

Core Workflows

  1. Route-Level Integration: Apply middleware to specific routes (e.g., API docs, admin panels):

    Route::middleware(['markdown'])->group(function () {
        Route::get('/admin/guide', 'AdminController@guide');
    });
    
  2. Controller-Level Integration: Use the MarkdownResponse trait in controllers:

    use Spatie\MarkdownResponse\MarkdownResponse;
    
    class DocsController extends Controller {
        use MarkdownResponse;
    
        public function show() {
            return $this->markdownResponse(view('docs'));
        }
    }
    
  3. Dynamic Markdown Generation: Combine with Laravel’s response macros for reusable logic:

    Response::macro('markdown', function ($content) {
        return response($content)->header('Content-Type', 'text/markdown');
    });
    
  4. API + Markdown Hybrid: Serve HTML to browsers and markdown to bots/AI via middleware:

    Route::get('/api/docs', function () {
        return view('api-docs')->header('X-API-Version', 'v1');
    })->middleware(\Spatie\MarkdownResponse\Middleware\MarkdownResponse::class);
    

Integration Tips

  • Parser Customization: Extend the default parser (e.g., for custom HTML-to-markdown rules):

    use Spatie\MarkdownResponse\Parsers\Parser;
    
    class CustomParser extends Parser {
        protected function parseTable($html) { /* ... */ }
    }
    

    Register in config/markdown-response.php:

    'parser' => \App\Parsers\CustomParser::class,
    
  • Conditional Markdown: Use middleware conditions to exclude routes:

    Route::get('/public', function () {
        return view('public');
    })->middleware(function ($request, $next) {
        if ($request->user()->isAdmin()) {
            return $next($request);
        }
        return response('Unauthorized.', 403);
    })->middleware(\Spatie\MarkdownResponse\Middleware\MarkdownResponse::class);
    
  • Testing: Mock the middleware in tests:

    $response = $this->withHeaders(['Accept' => 'text/markdown'])
        ->get('/docs')
        ->assertHeader('Content-Type', 'text/markdown');
    

Gotchas and Tips

Pitfalls

  1. Parser Limitations:

    • Complex HTML (e.g., nested tables, custom JS/CSS) may not convert cleanly. Test edge cases.
    • Avoid relying on parser output for critical data (e.g., tables with merged cells).
  2. Performance:

    • Markdown conversion adds overhead. Cache responses for static content:
      Route::get('/docs')->middleware([
          \Spatie\MarkdownResponse\Middleware\MarkdownResponse::class,
          \Illuminate\Cache\Middleware\AddQueuedResponseHeaders::class,
      ]);
      
  3. Header Conflicts:

    • Ensure Accept: text/markdown isn’t overridden by other middleware (e.g., API auth packages).
  4. Config Overrides:

    • Default allowed_routes in config is ['*']. Restrict to specific routes for security:
      'allowed_routes' => [
          'docs/*',
          'api/v1/guides/*',
      ],
      

Debugging

  • Check Headers: Use dd($request->header('Accept')) in middleware to verify the text/markdown header is received.

  • Parser Debugging: Log raw HTML before conversion:

    \Log::debug('Markdown HTML:', ['html' => $html]);
    
  • Middleware Order: Place MarkdownResponse after auth/locale middleware to avoid early termination.

Extension Points

  1. Custom Parsers: Override Spatie\MarkdownResponse\Parsers\Parser for domain-specific HTML (e.g., Markdown tables for financial data).

  2. Response Modifiers: Extend the middleware to add metadata:

    public function handle($request, Closure $next) {
        $response = $next($request);
        if ($this->shouldConvertToMarkdown($request)) {
            $response->header('X-Markdown-Source', $request->path());
        }
        return $response;
    }
    
  3. Fallback Logic: Handle unsupported HTML gracefully:

    'parser' => function () {
        return new Parser([
            'fallback' => function ($html) {
                return "[!] Could not convert HTML to Markdown: " . substr($html, 0, 50) . "...";
            },
        ]);
    },
    
  4. AI-Specific Headers: Add bot detection logic:

    public function shouldConvertToMarkdown($request) {
        return $request->header('Accept') === 'text/markdown'
            && in_array($request->userAgent(), ['ChatGPT', 'Googlebot']);
    }
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport