Skip to content

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:

json
{
  "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

LayoutUsage
template::layouts.appFull layout with navigation + footer

Available Partials

Partials are included automatically by the layouts:

PartialIncluded By
template::partials.navigationlayouts.app
template::partials.footerlayouts.app

Example Page Template

blade
@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>
@endsection

Custom 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 active

How it works

  1. Vite detects assets/app.css at build/dev time by scanning the templates folder
  2. The compiled <link> tag is injected via @templateStyles inside the <head>
  3. 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>:

blade
<head>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
    @templateStyles
</head>

Example assets/app.css

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.php

Example lang file

php
// 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

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
# .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::)DescriptionIn basic template
welcomeLanding page
dashboardDashboard (logged in)
termsTerms of service
auth/loginLogin
auth/registerRegistration
auth/forgot-passwordForgot password
auth/reset-passwordReset password
auth/verify-emailVerify email
auth/confirm-passwordConfirm password
components/discord-widgetDiscord widget
components/online-counterOnline user counter
components/session-modals-skinSession modals skin
news/indexNews overview
news/showNews detail
downloads/indexDownload page
pages/showStatic page
voting/indexVoting page
donation/indexDonation providers
donation/packagesDonation packages
donation/successPayment success
donation/cancelPayment cancelled
donation/redeem-epinE-Pin redeem
tickets/indexTicket list
tickets/createCreate ticket
tickets/showTicket detail + replies
profile/editProfile settings
profile/partials/update-profile-information-formProfile info form
profile/partials/update-password-formPassword change form
dashboard/silk-historySilk transaction history
dashboard/mapLive player map
webmall/indexWebmall page wrapper
livewire/webmallWebmall Livewire component
livewire/rankings/character-rankingCharacter ranking Livewire table
livewire/rankings/guild-rankingGuild ranking Livewire table
livewire/rankings/unique-rankingUnique ranking Livewire table
ranking/charactersCharacter ranking page
ranking/character-detailCharacter detail page
ranking/guildsGuild ranking page
ranking/guild-detailGuild detail page
ranking/uniquesUnique ranking page
ranking/partials/avatarAvatar equipment slots partial
ranking/partials/equipmentEquipment slots partial
errors/401Unauthorized
errors/402Payment required
errors/403Forbidden
errors/404Not found
errors/419Page expired
errors/429Too many requests
errors/500Server error
errors/503Maintenance / unavailable

Available Blade Directives

Templates can use these built-in Blade directives:

DirectiveDescription
@settings('key', 'default')Output a setting value
@settingsRegistrationOpen / @elsesettingsRegistrationOpen / @endsettingsRegistrationOpenConditional block: only shows if registration is open
@settingsEmailVerificationRequired / @elsesettingsEmailVerificationRequired / @endsettingsEmailVerificationRequiredConditional block: only shows if email verification is required
@templateView('view.name')Renders a view from the template:: namespace inline
@templateStylesInjects the compiled <link> tag for assets/app.css (empty if file doesn't exist)
@onlineCounterRenders the online user counter
@discordWidgetRenders the Discord widget

You can also use the helper class directly:

php
\App\Helpers\SettingHelper::get('key', 'default')
\App\Helpers\SettingHelper::frontendLanguages()

Creating a Custom Template

Step-by-Step Guide

  1. Download the starter template from Admin Panel → Templates → "Download Starter Template"
  2. Extract the ZIP — it contains a skeleton with all overridable files
  3. Edit only the Blade files you want to customize
  4. Remove files you don't want to override — they'll fall back to the basic template automatically
  5. Add assets/app.css if you need custom styles (Vite picks it up automatically)
  6. Add lang/{locale}/ files inside your template folder for custom translations
  7. Update template.json with your template's name, version, and author
  8. Re-ZIP the template folder
  9. Upload via Admin Panel → Templates → Upload
  10. 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.php

TIP

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:

RuleDescription
Valid ZIPMust be a valid ZIP archive
Max file size50 MB
Blade files requiredMust contain at least one .blade.php file
No executable PHPOnly .blade.php files are allowed — no .php files
Dangerous patterns blockedScanned 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 depth5 levels
Reserved nameCannot name a template basic
Slug validationOnly 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 (basic cannot be deleted)
  • Download Starter — Download a skeleton ZIP as a starting point for new templates

Caching

Cache KeyDescriptionTTL
template.activeName of the active template1 hour
template.resolved.{template}.{view}Resolved view path per template + view1 hour
Auto-clearCache is cleared automatically on activation, deactivation, and deletion

To manually clear the template cache:

bash
php artisan template:cache-clear

Troubleshooting

Template not showing changes after upload

Clear the template cache manually:

bash
php artisan template:cache-clear

Custom CSS not loading

  • Make sure the file is at assets/app.css inside your template folder (not css/app.css)
  • Restart the Vite dev server (npm run dev) after adding the file for the first time
  • Run npm run build for 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.

Released under the PolyForm Shield License 1.0.0.