Templates
SilkPanel CMS uses a template system that lets you fully customize the frontend appearance of your site. Templates are Blade-based, support partial overrides, custom styles, and custom translations — and can be managed entirely from the admin panel.
How Templates Work
Every template lives in resources/views/templates/ as a named folder. The basic template is always present and serves as the default fallback. Custom templates only need to include the files they want to override — anything missing automatically falls back to basic.
Directory Structure
resources/views/templates/
├── basic/ ← Default-Template (always present)
│ ├── template.json ← Metadata
│ ├── template.png ← Preview image (optional)
│ ├── assets/
│ │ └── app.css ← Custom CSS (Vite entry point, optional)
│ ├── lang/ ← Template-specific translations (optional)
│ │ ├── en/
│ │ │ └── my-keys.php
│ │ └── de/
│ │ └── my-keys.php
│ ├── layouts/
│ │ ├── app.blade.php ← Main layout (nav + footer + @yield('content'))
│ ├── partials/
│ │ ├── navigation.blade.php
│ │ └── footer.blade.php
│ ├── welcome.blade.php
│ ├── dashboard.blade.php
│ ├── terms.blade.php
│ ├── auth/
│ │ ├── login.blade.php
│ │ ├── register.blade.php
│ │ ├── forgot-password.blade.php
│ │ ├── reset-password.blade.php
│ │ ├── verify-email.blade.php
│ │ └── confirm-password.blade.php
│ ├── components/
│ │ ├── discord-widget.blade.php
│ │ └── online-counter.blade.php
│ ├── news/
│ │ ├── index.blade.php
│ │ └── show.blade.php
│ ├── downloads/
│ │ └── index.blade.php
│ ├── pages/
│ │ └── show.blade.php
│ ├── voting/
│ │ └── index.blade.php
│ ├── donation/
│ │ ├── index.blade.php
│ │ ├── packages.blade.php
│ │ ├── success.blade.php
│ │ ├── cancel.blade.php
│ │ └── redeem-epin.blade.php
│ ├── tickets/
│ │ ├── index.blade.php
│ │ ├── create.blade.php
│ │ └── show.blade.php
│ ├── profile/
│ │ ├── edit.blade.php
│ │ └── partials/
│ │ ├── update-profile-information-form.blade.php
│ │ └── update-password-form.blade.php
│ ├── dashboard/
│ │ ├── silk-history.blade.php
│ │ └── map.blade.php
│ ├── webmall/
│ │ └── index.blade.php
│ ├── livewire/
│ │ ├── webmall.blade.php
│ │ └── rankings/
│ │ ├── character-ranking.blade.php
│ │ ├── guild-ranking.blade.php
│ │ └── unique-ranking.blade.php
│ ├── errors/
│ │ ├── 401.blade.php
│ │ ├── 402.blade.php
│ │ ├── 403.blade.php
│ │ ├── 404.blade.php
│ │ ├── 419.blade.php
│ │ ├── 429.blade.php
│ │ ├── 500.blade.php
│ │ └── 503.blade.php
│ └── ranking/
│ ├── characters.blade.php
│ ├── character-detail.blade.php
│ ├── guilds.blade.php
│ ├── guild-detail.blade.php
│ ├── uniques.blade.php
│ └── partials/
│ ├── avatar.blade.php
│ └── equipment.blade.php
└── silkroad-gaming/ ← Example Custom-Template
├── template.json
├── assets/
│ └── app.css ← Custom CSS (Vite entry point)
├── lang/
│ ├── en/
│ │ └── silkroad-gaming.php
│ └── de/
│ └── silkroad-gaming.php
├── welcome.blade.php
├── layouts/
│ └── app.blade.php
└── ...TIP
The folder name determines the template slug. If no template.json exists, the folder name is used as the slug automatically.
Template Metadata
Each template should include a template.json file with metadata:
{
"name": "Silkroad Gaming",
"slug": "silkroad-gaming",
"version": "1.0.0",
"author": "Your Name",
"description": "A dark gaming-themed template with emerald/cyan accents and glassmorphism effects.",
"preview_image": "template.png"
}The preview_image field is optional and points to an image file inside the template folder that is displayed as a preview in the Admin Panel.
Blade Structure
All page templates use @extends with the template:: namespace. The variable $activeTemplate is automatically available in all views via a View Composer and contains the name of the currently active template.
Available Layouts
| Layout | Usage |
|---|---|
template::layouts.app | Full layout with navigation + footer |
Available Partials
Partials are included automatically by the layouts:
| Partial | Included By |
|---|---|
template::partials.navigation | layouts.app |
template::partials.footer | layouts.app |
Example Page Template
@extends('template::layouts.app')
@section('content')
<div class="container mx-auto py-8">
<h1>Welcome to {{ @settings('app_name', 'SilkPanel') }}</h1>
@settingsRegistrationOpen
<a href="/register">Create an Account</a>
@endsettingsRegistrationOpen
</div>
@endsectionCustom CSS per Template
Every template can ship its own CSS file at assets/app.css inside the template folder. This file is automatically compiled by Vite and injected into the layout via @templateStyles.
File location
resources/views/templates/my-template/
└── assets/
└── app.css ← Vite entry point — loaded only when this template is activeHow it works
- Vite detects
assets/app.cssat build/dev time by scanning the templates folder - The compiled
<link>tag is injected via@templateStylesinside the<head> - The styles are only applied when the corresponding template is active
Usage in layouts
The @templateStyles directive is already included in layouts/app.blade.php of the basic template. If you create a custom layout you need to add it manually inside <head>:
<head>
@vite(['resources/css/app.css', 'resources/js/app.js'])
@templateStyles
</head>Example assets/app.css
:root {
--brand-primary: #10b981;
}
.hero-gradient {
background: linear-gradient(135deg, #064e3b, #0c4a6e);
}TIP
After adding or removing an assets/app.css file you need to restart the Vite dev server (npm run dev) so it picks up the new entry point.
Template-Specific Translations
Templates can ship their own language files by placing them inside a lang/ folder inside the template directory. These files are automatically loaded by the TemplateServiceProvider and are available via the standard __() helper — no namespace prefix needed.
File location
resources/views/templates/my-template/
└── lang/
├── en/
│ └── my-template.php ← __('my-template.key')
├── de/
│ └── my-template.php
└── tr/
└── my-template.phpExample lang file
// lang/en/my-template.php
return [
'hero_title' => 'Welcome to the Ultimate Silkroad Experience',
'hero_subtitle' => 'Join thousands of players on our private server.',
'join_discord' => 'Join our Discord',
'features' => [
'title' => 'Why play here?',
'pvp' => 'Intense PvP',
],
];Usage in Blade
{{ __('my-template.hero_title') }}
{{ __('my-template.features.pvp') }}Fallback behavior
Laravel automatically falls back to the fallback_locale (set in config/app.php, default: en) when a key is missing for the current locale. You only need to provide all locales you want to support — English is sufficient to start.
Git integration
Placing lang files inside the template folder means a single .gitignore entry excludes both the views and the translations together:
# .gitignore — exclude a private template and all its assets/translations
resources/views/templates/my-private-template/Overridable Views
Templates can override the following views. Views marked with ✅ are included in the basic template.
View (template::) | Description | In basic template |
|---|---|---|
welcome | Landing page | ✅ |
dashboard | Dashboard (logged in) | ✅ |
terms | Terms of service | ✅ |
auth/login | Login | ✅ |
auth/register | Registration | ✅ |
auth/forgot-password | Forgot password | ✅ |
auth/reset-password | Reset password | ✅ |
auth/verify-email | Verify email | ✅ |
auth/confirm-password | Confirm password | ✅ |
components/discord-widget | Discord widget | ✅ |
components/online-counter | Online user counter | ✅ |
components/session-modals-skin | Session modals skin | ✅ |
news/index | News overview | ✅ |
news/show | News detail | ✅ |
downloads/index | Download page | ✅ |
pages/show | Static page | ✅ |
voting/index | Voting page | ✅ |
donation/index | Donation providers | ✅ |
donation/packages | Donation packages | ✅ |
donation/success | Payment success | ✅ |
donation/cancel | Payment cancelled | ✅ |
donation/redeem-epin | E-Pin redeem | ✅ |
tickets/index | Ticket list | ✅ |
tickets/create | Create ticket | ✅ |
tickets/show | Ticket detail + replies | ✅ |
profile/edit | Profile settings | ✅ |
profile/partials/update-profile-information-form | Profile info form | ✅ |
profile/partials/update-password-form | Password change form | ✅ |
dashboard/silk-history | Silk transaction history | ✅ |
dashboard/map | Live player map | ✅ |
webmall/index | Webmall page wrapper | ✅ |
livewire/webmall | Webmall Livewire component | ✅ |
livewire/rankings/character-ranking | Character ranking Livewire table | ✅ |
livewire/rankings/guild-ranking | Guild ranking Livewire table | ✅ |
livewire/rankings/unique-ranking | Unique ranking Livewire table | ✅ |
ranking/characters | Character ranking page | ✅ |
ranking/character-detail | Character detail page | ✅ |
ranking/guilds | Guild ranking page | ✅ |
ranking/guild-detail | Guild detail page | ✅ |
ranking/uniques | Unique ranking page | ✅ |
ranking/partials/avatar | Avatar equipment slots partial | ✅ |
ranking/partials/equipment | Equipment slots partial | ✅ |
errors/401 | Unauthorized | ✅ |
errors/402 | Payment required | ✅ |
errors/403 | Forbidden | ✅ |
errors/404 | Not found | ✅ |
errors/419 | Page expired | ✅ |
errors/429 | Too many requests | ✅ |
errors/500 | Server error | ✅ |
errors/503 | Maintenance / unavailable | ✅ |
Available Blade Directives
Templates can use these built-in Blade directives:
| Directive | Description |
|---|---|
@settings('key', 'default') | Output a setting value |
@settingsRegistrationOpen / @elsesettingsRegistrationOpen / @endsettingsRegistrationOpen | Conditional block: only shows if registration is open |
@settingsEmailVerificationRequired / @elsesettingsEmailVerificationRequired / @endsettingsEmailVerificationRequired | Conditional block: only shows if email verification is required |
@templateView('view.name') | Renders a view from the template:: namespace inline |
@templateStyles | Injects the compiled <link> tag for assets/app.css (empty if file doesn't exist) |
@onlineCounter | Renders the online user counter |
@discordWidget | Renders the Discord widget |
You can also use the helper class directly:
\App\Helpers\SettingHelper::get('key', 'default')
\App\Helpers\SettingHelper::frontendLanguages()Creating a Custom Template
Step-by-Step Guide
- Download the starter template from Admin Panel → Templates → "Download Starter Template"
- Extract the ZIP — it contains a skeleton with all overridable files
- Edit only the Blade files you want to customize
- Remove files you don't want to override — they'll fall back to the
basictemplate automatically - Add
assets/app.cssif you need custom styles (Vite picks it up automatically) - Add
lang/{locale}/files inside your template folder for custom translations - Update
template.jsonwith your template's name, version, and author - Re-ZIP the template folder
- Upload via Admin Panel → Templates → Upload
- Activate the template
TIP
You don't need to include every file. Only override what you want to change — everything else falls back to the basic template.
Starter Template Contents
The downloaded starter ZIP contains:
my-template/
├── template.json
├── welcome.blade.php
├── dashboard.blade.php
├── payment.blade.php
└── auth/
├── login.blade.php
├── register.blade.php
└── forgot-password.blade.phpTIP
The starter only includes the most common pages. If you want to customize layouts, partials, ranking views, or add custom CSS/translations, create the corresponding files manually in your template folder.
Upload Validation & Security
When uploading a ZIP template, SilkPanel performs strict validation:
| Rule | Description |
|---|---|
| Valid ZIP | Must be a valid ZIP archive |
| Max file size | 50 MB |
| Blade files required | Must contain at least one .blade.php file |
| No executable PHP | Only .blade.php files are allowed — no .php files |
| Dangerous patterns blocked | Scanned for: eval(), exec(), system(), shell_exec(), passthru(), popen(), proc_open(), backtick execution, file_put_contents(), remote file_get_contents() (http/https), variable include/require, base64_decode(), unserialize() |
| No path traversal | ../ is rejected |
| Max nesting depth | 5 levels |
| Reserved name | Cannot name a template basic |
| Slug validation | Only a-z, 0-9 and - allowed, max 64 characters, must start and end with an alphanumeric character |
Allowed Non-Blade Files
Templates may include these static file types:
.json, .png, .jpg, .jpeg, .gif, .svg, .webp, .ico, .css, .js
WARNING
Uploading a template named basic is not allowed. The basic template is a system template and cannot be overwritten or deleted.
Admin Panel (Filament)
The Template Manager is available in the admin panel under Configuration → Templates.
Features
- Upload — ZIP upload (max 50 MB) with automatic validation and installation. Re-uploading the same template slug overwrites the existing template (deleted + reinstalled).
- Template Cards — Visual grid with preview image, name, version, author, description, file count, and active badge
- Activate — One-click activation (cache is cleared automatically)
- Deactivate — Deactivate a template, falls back to root views
- Delete — Remove custom templates (
basiccannot be deleted) - Download Starter — Download a skeleton ZIP as a starting point for new templates
Caching
| Cache Key | Description | TTL |
|---|---|---|
template.active | Name of the active template | 1 hour |
template.resolved.{template}.{view} | Resolved view path per template + view | 1 hour |
| Auto-clear | Cache is cleared automatically on activation, deactivation, and deletion | — |
To manually clear the template cache:
php artisan template:cache-clearTroubleshooting
Template not showing changes after upload
Clear the template cache manually:
php artisan template:cache-clearCustom CSS not loading
- Make sure the file is at
assets/app.cssinside your template folder (notcss/app.css) - Restart the Vite dev server (
npm run dev) after adding the file for the first time - Run
npm run buildfor production
Translations not resolving
- File must be inside
lang/{locale}/inside your template folder - The locale must match the app locale (e.g.
en,de,tr) - Filename becomes the first segment of the key:
lang/en/foo.php→__('foo.key')
Upload rejected with "dangerous pattern" error
Make sure your Blade files don't contain any PHP functions like eval(), exec(), system(), or base64_decode(). Only standard Blade syntax and directives are allowed.
Fallback not working for missing files
Ensure the basic template has the file you expect to fall back to. Custom templates only fall back to files that exist in basic.