Creating a Custom Theme Package
This page moves the custom-theme folder from Creating a Store Theme into the Webkul\CustomTheme package. A package can be versioned and installed on other stores, and it's where the theme's Vite build, section types and image templates live on later pages.
What You'll Build
packages/Webkul/CustomTheme/
└── src/
├── Providers/
│ └── CustomThemeServiceProvider.php
└── Resources/
├── lang/
│ └── en/
│ └── app.php
└── views/
└── home/
└── index.blade.phpThe package keeps using the custom-theme entry in config/themes.php from Step 1 of the previous page. Add that entry now if you skipped it.
Step 1: Create the Service Provider
The provider publishes the package views into the theme's views_path.
File: packages/Webkul/CustomTheme/src/Providers/CustomThemeServiceProvider.php
<?php
namespace Webkul\CustomTheme\Providers;
use Illuminate\Support\ServiceProvider;
class CustomThemeServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../Resources/views' => resource_path('themes/custom-theme/views'),
], 'custom-theme-views');
}
}publishes() copies the views when you run vendor:publish. Skipping the Publish Step reads them straight from the package instead.
Step 2: Move the Views into the Package
Copy the theme folder's views directory into the package:
cp -r resources/themes/custom-theme/views/. packages/Webkul/CustomTheme/src/Resources/views/The home page is now at packages/Webkul/CustomTheme/src/Resources/views/home/index.blade.php; if you skipped the previous page, create it from Step 2 there. Package views follow the same rule as the theme folder: the path under Resources/views must match the Shop view's path.
Step 3: Add Translations
Later pages give the package strings of its own, such as section names and validation messages, in the custom-theme:: namespace.
File: packages/Webkul/CustomTheme/src/Resources/lang/en/app.php
<?php
return [
'sections' => [
'hero-banner' => 'Hero Banner',
'slides' => 'Slides',
'add-slide' => 'Add Slide',
'image' => 'Image',
'heading' => 'Heading',
'deals-carousel' => 'Deals Carousel',
'on-sale' => 'On Sale',
'services' => 'Services',
'free-shipping' => 'Free Shipping',
'free-shipping-info' => 'Free shipping on all orders',
],
'validation' => [
'password' => 'Password',
'strong-password' => 'Use at least 8 characters with an uppercase letter, a lowercase letter and a digit.',
],
];Load the directory at the end of the provider's boot() method:
File: packages/Webkul/CustomTheme/src/Providers/CustomThemeServiceProvider.php
<?php
namespace Webkul\CustomTheme\Providers;
use Illuminate\Support\ServiceProvider;
class CustomThemeServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*/
public function boot(): void
{
$this->publishes([
__DIR__.'/../Resources/views' => resource_path('themes/custom-theme/views'),
], 'custom-theme-views');
$this->loadTranslationsFrom(__DIR__.'/../Resources/lang', 'custom-theme');
}
}trans('custom-theme::app.sections.hero-banner') now reads the file. Add the same keys for every locale the store serves; see Localization.
Step 4: Autoload and Register the Package
Add the namespace to the psr-4 map of the root composer.json, beside the Webkul\\ entries already there:
File: composer.json
"autoload": {
"psr-4": {
"Webkul\\CustomTheme\\": "packages/Webkul/CustomTheme/src"
}
}Regenerate the autoloader, or Laravel can't load the provider:
composer dump-autoloadThen register the provider:
File: bootstrap/providers.php
<?php
use App\Providers\AppServiceProvider;
use Webkul\CustomTheme\Providers\CustomThemeServiceProvider;
return [
AppServiceProvider::class,
// ...
CustomThemeServiceProvider::class,
];Package Development covers package registration in depth.
Step 5: Publish the Views
Publish the package views into resources/themes/custom-theme/views, and clear the cache:
php artisan vendor:publish --provider="Webkul\CustomTheme\Providers\CustomThemeServiceProvider" --force
php artisan optimize:clear--force overwrites the published copies, so run it again after every change to a package view, and never edit the published copies.
Test It
php artisan vendor:publish --provider="Webkul\CustomTheme\Providers\CustomThemeServiceProvider" --forcecopiespackages/Webkul/CustomTheme/src/Resources/viewstoresources/themes/custom-theme/views. "No publishable resources" means the provider isn't autoloaded or registered (Step 4).php artisan tinker --execute="echo trans('custom-theme::app.sections.hero-banner');"printsHero Banner.- The home page of a channel running
custom-themeshows your template. - Change the package's home view, publish again with
--force, and reload: the change shows.
Skipping the Publish Step
Publishing leaves two copies of every view. Instead, register the package views under the theme code in the provider's boot() method, in place of the publishes() call; when a theme other than default is active, that namespace is searched before the Shop package:
$this->loadViewsFrom(__DIR__.'/../Resources/views', 'custom-theme');With custom-theme active, shop::home.index resolves to the package file directly. To use another namespace name, register it under that name and add 'views_namespace' => 'that-name' to the theme's entry.
- Delete the published copies, such as
resources/themes/custom-theme/views/home/index.blade.php, but keep the directory.views_pathis searched first, so a stale copy keeps answering, and it stays the place where a store can override one file of your package. - Components still need
views_path. The theme namespace isn't searched for Blade components, so an override of<x-shop::layouts>or any other component must be published there (keep apublishes()call for those files). - Not for the
defaulttheme. The shortcut is skipped when the active theme code isdefault.
The full order is in How Views Are Resolved.
Symlinking the Views
Republishing after every change is slow while you develop. Point the theme's views_path at the package views instead. Move any edits out of the published directory first, then run from the project root:
rm -rf resources/themes/custom-theme/views
ln -s $(pwd)/packages/Webkul/CustomTheme/src/Resources/views resources/themes/custom-theme/viewsAlternatively, iterate in resources/themes/custom-theme/views and copy the finished files into the package.
Things to Watch
views_pathalways wins. Published copies are searched before the package's own views, however those reach the finder.- Edit the package, not the published copy.
vendor:publish --forcereplaces the published files. - The theme gallery shows only the theme's
name. The screenshot, author, version and description come from the core filepackages/Webkul/Theme/src/Resources/catalog.php, read by the protectedWebkul\Theme\ThemeCatalog::catalogEntries(). To show yours, bind a subclass ofThemeCatalogwhosecatalogEntries()adds an entry under your theme code, with a remotescreenshotURL, since any other path is read from the admin build. - Symbolic links need extra setup on Windows.
Next Step
The package still reuses the default theme's build. Next, give it its own Tailwind CSS 4 and Vite build.
Continue to: Vite-Powered Theme Assets
