dominservice/laravel-theme-helper

A small Laravel utility to manage everything you usually put into the <head> and the closing of <body>, plus a pragmatic Schema.org JSON‑LD generator.

Maintainers

Package info

github.com/dominservice/laravel-theme-helper

pkg:composer/dominservice/laravel-theme-helper

Statistics

Installs: 7

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

3.1.0 2026-04-03 21:07 UTC

This package is auto-updated.

Last update: 2026-04-03 21:08:28 UTC


README

Packagist Latest Version Total Downloads Software License

A Laravel package for:

  • SEO and meta management,
  • asset registration and rendering,
  • breadcrumb and hreflang support,
  • JSON-LD / Schema.org generation,
  • sitemap and robots.txt generation helpers,
  • responsive image rendering with picture / AVIF / WebP / fallback support,
  • schema presets for common company and CMS pages,
  • theme registry and active theme selection,
  • public/admin theme context handling,
  • asset resolution for mix, vite, and static builds,
  • theme discovery through theme.json manifests.

It is designed to be the integration layer between your Laravel application and one or more frontend themes, while still providing the usual <head> and </body> helpers.

Table of contents

Requirements

  • PHP 8.1+
  • Laravel 10.x / 11.x / 12.x / 13.x
  • spatie/schema-org
  • Optional: mcamara/laravel-localization

Installation

composer require dominservice/laravel-theme-helper

Optional hreflang integration:

composer require mcamara/laravel-localization

Publish config if you want to define themes globally:

php artisan vendor:publish --tag=config

This publishes:

  • config/theme-helper.php

Quick start

use Dominservice\LaravelThemeHelper\Facades\Theme;

Theme::meta()
    ->setTitle('DSO-IT', config('app.name'))
    ->setDescription('Software house and custom web systems')
    ->setCanonical(url()->current());

Theme::assets()->useTheme('public');

echo Theme::renderHead();
echo Theme::renderBodyEnd();

Switch public theme dynamically:

Theme::useTheme('public-metronic', 'public');
Theme::assets()->useTheme('public');

Theme system

Contexts

The package supports theme contexts.

Typical usage:

  • admin
  • public

Rules are application-defined, but a common setup is:

  • admin always uses a fixed admin theme
  • public can use one of many declared themes

Theme config

Published config structure:

return [
    'contexts' => [
        'admin' => [
            'default_theme' => 'admin',
        ],
        'public' => [
            'default_theme' => 'public',
        ],
    ],

    'themes' => [
        'admin' => [
            'label' => 'Admin',
            'context' => 'admin',
            'driver' => 'static',
            'layout' => null,
            'view_path' => null,
            'entries' => [
                'css' => [],
                'js' => [],
            ],
            'meta' => [],
        ],
    ],

    'discovery' => [
        'enabled' => false,
        'manifest_filename' => 'theme.json',
        'scan_paths' => [],
    ],
];

Theme discovery with theme.json

The package can discover themes from one or more directories. This lets you keep your application-defined themes in config while also supporting uploaded or shipped themes described by a manifest.

Example config:

'discovery' => [
    'enabled' => true,
    'manifest_filename' => 'theme.json',
    'scan_paths' => [
        resource_path('themes'),
        base_path('themes'),
    ],
],

Example theme.json:

{
  "key": "classic",
  "label": "Classic",
  "context": "public",
  "driver": "mix",
  "layout": "themes.classic.layouts.app",
  "view_path": "resources/views/themes/classic",
  "manifest_path": "public/themes/classic/mix-manifest.json",
  "deploy_target": "public/themes/classic",
  "entries": {
    "css": ["themes/classic/app.css"],
    "js": ["themes/classic/app.js"]
  },
  "description": "Base public theme",
  "version": "1.0.0",
  "author": {
    "name": "Domin Software"
  },
  "settings": {
    "supports_upload": true
  }
}

If a theme key already exists in config, config has priority over discovered manifests. This makes discovery safe for local overrides.

Drivers

Supported asset drivers:

  • static
  • mix
  • vite

Theme switching

Theme::useTheme('public-metronic', 'public');
Theme::useTheme('admin', 'admin');

$theme = Theme::currentTheme('public');
$layout = $theme->layout();
$driver = $theme->driver();
$description = $theme->description();
$previewImage = $theme->previewImage();

Structured data

Multiple schema types on one page

Google supports multiple structured data items on the same page when they describe user-visible content on that page. This is useful for pages that need more than one schema type at once, for example:

  • Organization
  • WebSite
  • WebPage
  • BreadcrumbList
  • Product
  • Service
  • FAQPage

This package supports that pattern through @graph.

Simple example:

echo Theme::structuredGraph([
    [
        '@type' => 'Organization',
        'name' => 'Domin Software',
        'url' => 'https://example.com',
    ],
    [
        '@type' => 'WebPage',
        'name' => 'CRM systems',
        'url' => 'https://example.com/crm',
    ],
    [
        '@type' => 'BreadcrumbList',
        'itemListElement' => [
            [
                '@type' => 'ListItem',
                'position' => 1,
                'name' => 'Home',
                'item' => 'https://example.com',
            ],
        ],
    ],
]);

Schema collection API

$schemas = Theme::schemaCollection()
    ->organization([
        'name' => 'Domin Software',
        'url' => 'https://example.com',
    ])
    ->product([
        'name' => 'CMS Starter',
    ])
    ->service([
        'name' => 'CRM implementation',
        'serviceType' => 'Custom CRM development',
    ]);

echo Theme::structuredGraph($schemas);

This lets you output one graph that contains separate entities for organization data, product data, services, breadcrumbs or page-level metadata.

Schema presets

For common business pages you often want a repeatable graph instead of building every entity manually.

Available preset keys:

  • organization
  • website
  • home_page
  • web_page
  • service_page
  • product_page
  • faq_page

Example:

$schemas = Theme::schemaPreset('service_page', [
    'organization' => [
        'name' => 'Domin Software',
        'url' => 'https://example.com',
        'logo' => 'https://example.com/logo.png',
    ],
    'web_page' => [
        'name' => 'CRM systems',
        'url' => 'https://example.com/crm',
        'description' => 'Custom CRM systems for companies',
    ],
    'service' => [
        'name' => 'CRM implementation',
        'serviceType' => 'Custom CRM development',
    ],
    'breadcrumbs' => [
        ['@type' => 'ListItem', 'position' => 1, 'name' => 'Home', 'item' => 'https://example.com'],
        ['@type' => 'ListItem', 'position' => 2, 'name' => 'CRM', 'item' => 'https://example.com/crm'],
    ],
]);

echo Theme::structuredGraph($schemas);

Sitemap

The package includes a builder for XML sitemaps. It supports:

  • standard urlset,
  • sitemapindex,
  • image sitemap extensions,
  • alternate locale URLs through xhtml:link,
  • configurable default changefreq and priority.

Example:

$xml = Theme::sitemap()
    ->addUrl('https://example.com/', [
        'lastmod' => now(),
        'changefreq' => 'daily',
        'priority' => '1.0',
        'alternates' => [
            ['hreflang' => 'pl-PL', 'href' => 'https://example.com/'],
            ['hreflang' => 'en-US', 'href' => 'https://example.com/en'],
        ],
        'images' => [
            [
                'loc' => 'https://example.com/storage/hero.webp',
                'title' => 'Homepage hero image',
            ],
        ],
    ])
    ->renderUrlset();

For sitemap indexes:

$index = Theme::sitemap()
    ->addSitemap('https://example.com/sitemap-pages.xml')
    ->addSitemap('https://example.com/sitemap-blog.xml')
    ->renderIndex();

Robots.txt

The package also includes a builder for robots.txt.

Example:

$robots = Theme::robots()
    ->addGroup('*', allow: ['/'], disallow: ['/admin', '/login'])
    ->addSitemap('https://example.com/sitemap.xml')
    ->setHost('example.com')
    ->render();

Responsive images

The package includes a small renderer for SEO-friendly responsive images with modern formats.

Example using explicit <source> nodes:

echo Theme::images()->picture([
    [
        'type' => 'image/avif',
        'srcset' => asset('themes/classic/img/about.avif'),
        'media' => '(min-width: 768px)',
    ],
    [
        'type' => 'image/webp',
        'srcset' => asset('themes/classic/img/about.webp'),
    ],
], [
    'src' => asset('themes/classic/img/about.jpg'),
    'alt' => 'About section visual',
    'width' => 1400,
    'height' => 1040,
]);

Or using a simpler format map:

echo Theme::images()->pictureFromFormats([
    'avif' => asset('themes/classic/img/about.avif'),
    'webp' => asset('themes/classic/img/about.webp'),
    'jpg' => asset('themes/classic/img/about.jpg'),
], [
    'alt' => 'About section visual',
    'width' => 1400,
    'height' => 1040,
]);

API

Main facade methods:

Theme::meta();
Theme::assets();
Theme::breadcrumbs();
Theme::hreflang();
Theme::sitemap();
Theme::robots();
Theme::schemaPreset('home_page', [...]);
Theme::images();
Theme::registry();
Theme::contexts();
Theme::currentTheme('public');
Theme::useTheme('classic', 'public');
Theme::structured([...]);
Theme::schemaCollection();
Theme::structuredGraph([...]);
Theme::renderHead();
Theme::renderBodyEnd();

Blade integration

<head>
    {!! Theme::renderHead() !!}
</head>
<body>
    @yield('content')

    {!! Theme::renderBodyEnd() !!}
</body>

Helpers

Available helpers:

  • theme_structured_data(array $data, array $options = [])
  • theme_structured_graph(array $schemas, array $options = [])
  • theme_sitemap()
  • theme_robots()
  • theme_schema_preset(string $preset, array $data = [])
  • theme_picture(array $sources, array $imgAttributes = [], array $pictureAttributes = [])
  • theme_current_theme(string $context = 'public')
  • theme_current_layout(string $context = 'public')
  • theme_use(string $themeKey, ?string $context = null)

Deployment notes

Theme definitions can now also carry:

  • build_config_path
  • manifest_path
  • hot_file_path
  • deploy_target
  • source_path
  • preview_image
  • settings

This makes the package a good base for future theme upload, import/export and theme management panels without forcing a specific storage model today.

Testing

Run package tests:

vendor/bin/phpunit

License

MIT