Textstem has 13 features that are off until a site turns them on. Most need one .env switch, and some also need extra packages, a queue worker or a host-app change.

At a glance

Feature Switch Main requirement
Form Data FORMS_ENABLED=true Migration; Livewire and Tailwind on public pages
Live page screenshots TEXTSTEM_PAGE_SCREENSHOTS=true spatie/browsershot, Puppeteer, headless Chrome, queue worker
React rendering TEXTSTEM_REACT_RENDERING=true Inertia and React frontend; textstem:install-react
MCP server MCP_ENABLED=true None (endpoints are public)
Page cache WRANGLER_CACHE_ENABLE=true A working cache store
External search (Scout) searchable in config/pages.php, SCOUT_DRIVER A Scout engine such as Meilisearch or Algolia
Real-time updates BROADCAST_DRIVER Pusher, Reverb or Soketi, and Laravel Echo in the host app
Scheduled security audit Tools → Security Audit in the admin Scheduler cron
AI description button openai.generate_description in config An AI provider key
AI image comparison images.comparison_driver = openai OpenAI key
Analyse on save ACCESSIBILITY_CHECK_ON_SAVE, SEO_CHECK_ON_SAVE Queue worker recommended
Admin dark mode DARK_MODE=true None
Redis TEXTSTEM_USE_REDIS=true A Redis server and PHP Redis client

AI features are on by default but do nothing until a provider key is set. That setup is covered under AI features below.

Form Data

Editors build forms in the admin, with steps, show-if rules and file uploads, then place them on pages with the Active Form block. Answers are kept in the admin, and can be emailed to staff, exported as CSV or posted to a webhook. While it's off, none of its tables, routes, menus, blocks or schedules load.

Needs

  • Livewire scripts loaded on public pages
  • The package's form views in Tailwind's content, plus the @tailwindcss/forms plugin
  • A mailer and a queue worker, for staff emails and sender copies
  • A private writable disk for uploads (FORMS_UPLOAD_DISK, default local)
  • Optional: Cloudflare Turnstile keys (TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY) for the robot check
  • Optional: FORMS_VISITOR_GUARD and FORMS_VISITOR_LOGIN_ROUTE for forms limited to signed-in visitors

Turn it on

FORMS_ENABLED=true
php artisan migrate
php artisan config:cache   # only if config is cached

The migration adds the textstem_forms* tables, four Form Data permissions and a forms-manager role. Old answers are pruned daily at 03:45 by forms:prune-submissions.

Live page screenshots

The Pages admin's Cards and Columns views show a picture of each page, refreshed in the background a few seconds after each save. With screenshots off, each card shows the page's SEO image, else its first image, else an icon.

Needs

  • spatie/browsershot (Composer) and puppeteer (npm) in the host app
  • Node.js 18+ and chrome-headless-shell, installed as the queue worker's user
  • Chrome's system libraries and fonts on Linux
  • A running queue worker; captures don't run with QUEUE_CONNECTION=sync
  • About 150 to 300 MB of memory per capture

Turn it on

composer require spatie/browsershot
npm install puppeteer
npx puppeteer browsers install chrome-headless-shell
TEXTSTEM_PAGE_SCREENSHOTS=true
php artisan textstem:page-screenshots --check
php artisan textstem:page-screenshots --all
php artisan queue:restart

Set TEXTSTEM_SCREENSHOT_NODE, TEXTSTEM_SCREENSHOT_NPM or TEXTSTEM_SCREENSHOT_CHROME when those aren't on the worker's PATH. On Herd, also set TEXTSTEM_SCREENSHOT_IGNORE_HTTPS_ERRORS=true.

React rendering

Public pages render with Inertia and React instead of Blade. The admin panel stays Blade and Livewire. The post-login redirect becomes role-aware, and the admin bar keeps up with client-side navigation.

Needs

  • An Inertia and React frontend: Laravel's React starter kit, or @inertiajs/react, react, react-dom, @vitejs/plugin-react and inertiajs/inertia-laravel added by hand
  • Jetstream's Inertia option won't work, because it's Vue
  • No team management unless you build it
  • A React component for each custom Wrangler component

Turn it on

composer require inertiajs/inertia-laravel   # if not already installed
php artisan textstem:install-react

The command publishes the React source to resources/js/ and can add TEXTSTEM_REACT_RENDERING=true to .env. Then route textstem/* pages to PublicLayout in resources/js/app.tsx, add textstem: TextstemSharedProps to sharedPageProps in resources/js/types/global.d.ts, and run npm run build.

MCP server

The site's content is exposed to AI assistants such as Claude Desktop and Cursor over the Model Context Protocol. Built-in resources cover site info, published public pages and published posts. You can add your own tools and resources.

Needs

  • Nothing to install
  • Care with what you expose: the /mcp endpoints need no login, so expose only public content

Turn it on

MCP_ENABLED=true
MCP_PATH=mcp                # default
MCP_RATE_LIMIT=60           # requests per minute per IP
MCP_SITE_DESCRIPTION="A short description of the site"

This registers GET /mcp and POST /mcp. To add your own, run php artisan textstem:make:mcp-tool {name} or php artisan textstem:make:mcp-resource {name}.

Page cache

Rendered public pages are cached, so repeat visits skip rendering.

Needs

  • A working cache store (Redis recommended in production)

Turn it on

WRANGLER_CACHE_ENABLE=true
WRANGLER_CACHE_TTL=3600          # seconds
WRANGLER_CACHE_MODE=private      # Cache-Control: public or private
WRANGLER_CACHE_WITHQUERY=false   # true caches each query string separately

Clear it with php artisan cache:manage clear.

External search (Scout)

Pages and posts are indexed in an external engine for public site search. A page's index entry includes the text of its enabled blocks. The admin's own search at /textstem/search uses the database and doesn't need this.

Needs

  • A Scout engine, such as Meilisearch, Algolia or Typesense, with its client package
  • A front-end search page in the host app

Turn it on

Create config/pages.php in the host app. The page model reads pages.searchable:

return [
    'searchable' => 1,
];
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://localhost:7700
MEILISEARCH_KEY=your-key
php artisan scout:import "Medialight\Textstem\Models\WranglerPage"
php artisan scout:import "Medialight\Textstem\Models\Post"

Only enabled, public, listed pages are indexed.

Real-time updates

The admin updates live over WebSockets when models change or AI jobs finish, instead of polling.

Needs

  • A broadcast server: Pusher, Laravel Reverb or Soketi
  • Laravel Echo set up in the host app's JavaScript
  • A queue worker

Turn it on

BROADCAST_DRIVER=pusher
PUSHER_APP_ID=...
PUSHER_APP_KEY=...
PUSHER_APP_SECRET=...
PUSHER_APP_CLUSTER=mt1

Then set up Echo in resources/js/bootstrap.js and rebuild. The package registers its own private textstem.* channels.

Scheduled security audit

textstem:security-audit runs on a schedule and can email a report.

Needs

  • The scheduler cron (php artisan schedule:run every minute)
  • A mailer, for notifications

Turn it on

In the admin, open Tools → Security Audit and turn on the schedule. Choose daily or weekly (Wednesdays), a time (default 02:00), and whether to email always or only on alerts, and to which addresses.

AI features

All AI calls go through one service, which handles content generation, SEO analysis, accessibility checks, image comparison and the chat assistant. AI is on by default (OPENAI_ENABLED=true) but does nothing until a provider key is set.

Needs

  • An OpenAI or Anthropic API key
  • A queue worker, since AI jobs are queued by default (OPENAI_USE_QUEUE=true)

Turn it on

# OpenAI
TEXTSTEM_AI_PROVIDER=openai
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4o-mini

# or Anthropic
TEXTSTEM_AI_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
ANTHROPIC_MODEL=claude-haiku-4-5-20251001

TEXTSTEM_AI_MODEL overrides the model for every feature.

Three AI extras are each off until you set them:

Extra Turn it on Needs
Generate description button in editors 'generate_description' => true under openai in config/textstemapp.php Provider key
AI image comparison 'comparison_driver' => 'openai' under images in config/textstemapp.php (default fingerprint, no AI) OpenAI key
Image descriptions with Google Cloud Vision GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json A Google Cloud service account with Vision enabled

Analyse on save

Accessibility and SEO analysis normally run on demand. With these on, each save starts an analysis.

Needs

  • A queue worker, so saves don't wait (ACCESSIBILITY_USE_QUEUE and SEO_USE_QUEUE default to true)
  • A provider key if SEO_USE_AI=true

Turn it on

ACCESSIBILITY_CHECK_ON_SAVE=true
SEO_CHECK_ON_SAVE=true

Admin dark mode

Admin users can choose light, dark or their system setting.

Needs: nothing.

Turn it on: DARK_MODE=true

Redis

The package uses Redis for caching and queues.

Needs

  • A Redis server
  • The phpredis extension or the predis/predis package, and REDIS_* connection settings

Turn it on: TEXTSTEM_USE_REDIS=true

Sources

  • config/textstem.php, src/TextstemServiceProvider.php and src/Providers/TextstemFormsServiceProvider.php in medialight/textstem-laravel
  • docs/installation.md, docs/mcp.md, docs/search.md, docs/broadcasting.md and docs/ai-provider.md in the same repository
esc