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/globals-widgetLatest globals widget (@globalsWidget)✅
components/session-modals-skinSession modals skin✅
components/server-timeServer time (@serverTime)✅
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
@globalsWidget / @globalsWidget(10)Renders the latest global (yell) chat messages. iSRO only — renders nothing on other versions
@serverTimeRenders the current server time

You can also use the helper class directly:

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

Globals Widget ​

@globalsWidget renders a compact, responsive list of the latest global (yell) chat messages. It is designed to be dropped into a sidebar or a wider main content column — it fills the width of its container, so it works in either spot. Use it in any template or in the root (no-template) views.

blade
@globalsWidget               {{-- latest 10 messages (default) --}}
@globalsWidget(5)            {{-- latest 5 messages --}}
<x-globals-widget :limit="8" />
  • iSRO only. On vSRO (and any non-iSRO version) it renders nothing, because there is no standard table to read global messages from. A configurable custom source (via Page Settings) is planned for later.
  • Each character name links to its ranking profile at /ranking/characters/{name} (e.g. /ranking/characters/devsome).
  • The limit is clamped between 1 and 50, and results are cached for 60 seconds.
  • To match your template's look, override components/globals-widget.blade.php inside your template folder. The view receives $globals (a collection of { CharName, CharID, RefObjID, Comment, EventTime }) and $available (bool). If you don't override it, the base styling is used automatically.

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.