Skip to content

Theme Options ​

Theme options allow you to customize your theme's appearance and functionality through the admin panel. This documentation covers how to create, manage, and use theme options in Thesky9 CMS.

Basic Usage of Theme Options ​

Adding Theme Options ​

You can add theme options in your theme's functions/theme-options.php file. There are two approaches to creating theme options: using the array-based approach or using the fluent API with dedicated classes.

php
use Botble\Theme\ThemeOption\ThemeOptionSection;
use Botble\Theme\ThemeOption\Fields\TextField;
use Botble\Theme\ThemeOption\Fields\MediaImageField;

app('events')->listen(RenderingThemeOptionSettings::class, function (): void {
    ThemeOption::setSection(
        ThemeOptionSection::make('opt-text-subsection-general')
            ->title(__('General'))
            ->description(__('General settings'))
            ->icon('ti ti-home')
            ->priority(0)
            ->fields([
                TextField::make()
                    ->name('copyright')
                    ->label(__('Copyright'))
                    ->defaultValue('© ' . now()->year . ' Your Company. All rights reserved.')
                    ->placeholder(__('Change copyright'))
                    ->maxLength(120)
                    ->helperText(__('Copyright on footer of site')),
            ])
    );

    ThemeOption::setSection(
        ThemeOptionSection::make('opt-text-subsection-logo')
            ->title(__('Logo'))
            ->description(__('Change logo'))
            ->icon('ti ti-photo')
            ->priority(1)
            ->fields([
                MediaImageField::make()
                    ->name('logo')
                    ->label(__('Logo'))
                    ->helperText(__('Upload your logo here')),
            ])
    );
});

Approach 2: Using Array-Based Configuration ​

php
app('events')->listen(RenderingThemeOptionSettings::class, function (): void {
    theme_option()
        ->setSection([ // Set section with no field
            'title' => __('General'),
            'desc' => __('General settings'),
            'id' => 'opt-text-subsection-general',
            'subsection' => true,
            'icon' => 'ti ti-home',
        ])
        ->setSection([ // Set section with some fields
            'title' => __('Logo'),
            'desc' => __('Change logo'),
            'id' => 'opt-text-subsection-logo',
            'subsection' => true,
            'icon' => 'ti ti-photo',
            'fields' => [
                [
                    'id' => 'logo',
                    'type' => 'mediaImage',
                    'label' => __('Logo'),
                    'attributes' => [
                        'name' => 'logo',
                        'value' => null,
                    ],
                ],
            ],
        ])
        ->setField([ // Set field for section
            'id' => 'copyright',
            'section_id' => 'opt-text-subsection-general',
            'type' => 'text',
            'label' => __('Copyright'),
            'attributes' => [
                'name' => 'copyright',
                'value' => '© ' . now()->year . ' Your Company. All rights reserved.',
                'options' => [
                    'class' => 'form-control',
                    'placeholder' => __('Change copyright'),
                    'data-counter' => 120,
                ]
            ],
            'helper' => __('Copyright on footer of site'),
        ]);
});

Displaying Theme Options ​

To display theme options in your theme, use the theme_option() helper function:

blade
{{-- For plain text --}}
{{ theme_option('option_name') }}

{{-- For HTML content --}}
{!! theme_option('option_name') !!}

{{-- With default value if option is not set --}}
{{ theme_option('option_name', 'Default value') }}

Checking if a Theme Option Exists ​

php
if (theme_option('option_name')) {
    // Option exists and has a non-empty value
}

Saving Theme Options ​

You can programmatically set theme options:

php
Theme::setOption('option_name', 'option_value');
Theme::saveOptions(); // Save to database

Supported Fields ​

Thesky9 CMS supports various field types for theme options. You can use either the object-oriented approach (recommended) or the array-based approach.

Text Field ​

Object-Oriented Approach ​

php
use Botble\Theme\ThemeOption\Fields\TextField;

TextField::make()
    ->name('field_name')
    ->label(__('Field label'))
    ->defaultValue('Default value')
    ->placeholder(__('Placeholder (optional)'))
    ->maxLength(120)
    ->helperText(__('Helper text (optional)'))

Array-Based Approach ​

php
->setField([
    'id' => 'field_name',
    'section_id' => 'opt-text-subsection-section-id',
    'type' => 'text', // text, password, email, number
    'label' => __('Field label'),
    'attributes' => [
        'name' => 'field_name',
        'value' => null, // default value
        'options' => [
            'class' => 'form-control',
            'placeholder' => __('Placeholder (optional)'),
            'data-counter' => 120,
        ]
    ],
    'helper' => __('Helper text (optional)'),
])

Select Field ​

Object-Oriented Approach ​

php
use Botble\Theme\ThemeOption\Fields\SelectField;

SelectField::make()
    ->name('field_name')
    ->label(__('Field label'))
    ->options([
        0 => __('No'),
        1 => __('Yes'),
    ])
    ->defaultValue(1)
    ->helperText(__('Helper text (optional)'))

Array-Based Approach ​

php
->setField([
    'id' => 'field_name',
    'section_id' => 'opt-text-subsection-section-id',
    'type' => 'select', // select or customSelect
    'label' => __('Field label'),
    'attributes' => [
        'name' => 'field_name',
        'data' => [ // Array options for select
            0 => __('No'),
            1 => __('Yes'),
        ],
        'value' => null, // default value
        'options' => [
            'class' => 'form-control',
        ],
    ],
])

Media Image Field ​

Object-Oriented Approach ​

php
use Botble\Theme\ThemeOption\Fields\MediaImageField;

MediaImageField::make()
    ->name('banner_ads')
    ->label(__('Banner Image'))
    ->helperText(__('Select an image for the banner'))

Array-Based Approach ​

php
->setField([
    'id' => 'banner_ads',
    'section_id' => 'opt-text-subsection-section-id',
    'type' => 'mediaImage',
    'label' => __('Banner Image'),
    'attributes' => [
        'name' => 'banner_ads',
        'value' => null,
    ],
    'helper' => __('Select an image for the banner'),
])

Editor Field ​

Object-Oriented Approach ​

php
use Botble\Theme\ThemeOption\Fields\EditorField;

EditorField::make()
    ->name('field_name')
    ->label(__('Field label'))
    ->defaultValue('<p>Default content</p>')
    ->rows(10)
    ->helperText(__('Helper for this field (optional)'))

Array-Based Approach ​

php
->setField([
    'id' => 'field_name',
    'section_id' => 'opt-text-subsection-section-id',
    'type' => 'editor',
    'label' => __('Field label'),
    'attributes' => [
        'name' => 'field_name',
        'value' => null, // Default value
        'options' => [ // Optional
            'class' => 'form-control theme-option-textarea',
            'row' => '10',
        ],
    ],
    'helper' => __('Helper for this field (optional)'),
])

Radio Field ​

Object-Oriented Approach ​

php
use Botble\Theme\ThemeOption\Fields\RadioField;

RadioField::make()
    ->name('field_name')
    ->label(__('Field label'))
    ->options([
        0 => __('No'),
        1 => __('Yes'),
    ])
    ->defaultValue(1)
    ->inline(true) // Display options inline
    ->helperText(__('Helper for this field (optional)'))

Array-Based Approach ​

php
->setField([
    'id' => 'field_name',
    'section_id' => 'opt-text-subsection-section-id',
    'type' => 'onOff',
    'label' => __('Field label'),
    'attributes' => [
        'name' => 'field_name',
        'value' => 1,
        'data' => [
            0 => __('No'),
            1 => __('Yes'),
        ],
        'options' => [],// Optional
    ],
    'helper' => __('Helper for this field (optional)'),
]);

Custom Radio Field ​

Object-Oriented Approach ​

php
use Botble\Theme\ThemeOption\Fields\RadioField;

RadioField::make()
    ->name('field_name')
    ->label(__('Field label'))
    ->options([
        'left' => __('Left'),
        'right' => __('Right'),
        'center' => __('Center'),
    ])
    ->defaultValue('left')
    ->helperText(__('Helper for this field (optional)'))

Array-Based Approach ​

php
->setField([
    'id' => 'field_name',
    'section_id' => 'opt-text-subsection-section-id',
    'type' => 'customRadio',
    'label' => __('Field label'),
    'attributes' => [
        'name' => 'field_name',
        'values' => [
            ['left', __('Left')],
            ['right', __('Right')],
            ['center', __('Center')],
        ],
        'selected' => 'left',
        'options' => [],// Optional
    ],
    'helper' => __('Helper for this field (optional)'),
]);

Custom Select Field ​

Object-Oriented Approach ​

php
use Botble\Theme\ThemeOption\Fields\SelectField;

SelectField::make()
    ->name('field_name')
    ->label(__('Field label'))
    ->options([
        'option1' => __('Option 1'),
        'option2' => __('Option 2'),
        'option3' => __('Option 3'),
    ])
    ->defaultValue('option1')
    ->helperText(__('Helper for this field (optional)'))

Array-Based Approach ​

php
->setField([
    'id' => 'field_name',
    'section_id' => 'opt-text-subsection-section-id',
    'type' => 'customSelect',
    'label' => __('Field label'),
    'attributes' => [
        'values' => [
            ['field_name[]', 'option1', __('Option 1')],
            ['field_name[]', 'option2', __('Option 2')],
            ['field_name[]', 'option3', __('Option 3')],
        ],
        'name' => 'field_name',
    ],
    'helper' => __('Helper for this field (optional)'),
]);

Repeater Field ​

Object-Oriented Approach ​

php
use Botble\Theme\ThemeOption\Fields\RepeaterField;
use Botble\Theme\ThemeOption\Fields\TextField;
use Botble\Theme\ThemeOption\Fields\MediaImageField;
use Botble\Theme\ThemeOption\Fields\TextareaField;

RepeaterField::make()
    ->name('options_name')
    ->label(__('Repeater fields'))
    ->fields([
        TextField::make()
            ->name('text')
            ->label(__('Text'))
            ->maxLength(255),

        MediaImageField::make()
            ->name('image')
            ->label(__('Image')),

        TextareaField::make()
            ->name('textarea')
            ->label(__('Textarea'))
            ->rows(3)
            ->maxLength(255),
    ])

Array-Based Approach ​

php
->setField([
    'id'         => 'options_name',
    'section_id' => 'opt-text-subsection-general',
    'type'       => 'repeater',
    'label'      => __('Repeater fields'),
    'attributes' => [
        'name'   => 'options_name',
        'value'  => null,
        'fields' => [
            [
                'type'       => 'text',
                'label'      => __('Text'),
                'attributes' => [
                    'name'    => 'text',
                    'value'   => null,
                    'options' => [
                        'class'        => 'form-control',
                        'data-counter' => 255,
                    ],
                ],
            ],
            [
                'type'       => 'mediaImage',
                'label'      => __('Image'),
                'attributes' => [
                    'name'  => 'image',
                    'value' => null,
                ],
            ],
            [
                'type'       => 'textarea',
                'label'      => __('Textarea'),
                'attributes' => [
                    'name'    => 'textarea',
                    'value'   => null,
                    'options' => [
                        'class'        => 'form-control',
                        'data-counter' => 255,
                        'rows'         => 3,
                    ],
                ],
            ],
        ],
    ],
])

Additional Field Types ​

Color Picker ​

php
use Botble\Theme\ThemeOption\Fields\ColorField;

ColorField::make()
    ->name('primary_color')
    ->label(__('Primary Color'))
    ->defaultValue('#AF0F26')

Icon Selector ​

php
use Botble\Theme\ThemeOption\Fields\IconField;

IconField::make()
    ->name('social_icon')
    ->label(__('Social Icon'))
    ->defaultValue('ti ti-brand-facebook')

UI Selector ​

php
use Botble\Theme\ThemeOption\Fields\UiSelectorField;

UiSelectorField::make()
    ->name('layout_style')
    ->label(__('Layout Style'))
    ->options([
        'layout-1' => [
            'label' => __('Layout 1'),
            'image' => Theme::asset()->url('images/layouts/layout-1.png'),
        ],
        'layout-2' => [
            'label' => __('Layout 2'),
            'image' => Theme::asset()->url('images/layouts/layout-2.png'),
        ],
    ])
    ->defaultValue('layout-1')

Shared Fields (Multi-language) ​

When using the Language plugin, theme options are stored per language by default. This means every field gets a locale-specific key (e.g., theme-shofy-fr-logo for French). However, visual/design properties like colors, logos, and layout styles typically don't need per-language values.

You can mark fields or entire sections as shared so they are stored once and used across all languages.

Marking Individual Fields as Shared ​

Object-Oriented Approach ​

php
use Botble\Theme\ThemeOption\Fields\ColorField;

ColorField::make()
    ->name('primary_color')
    ->label(__('Primary Color'))
    ->defaultValue('#AF0F26')
    ->shared()

Array-Based Approach ​

php
->setField([
    'id' => 'primary_color',
    'section_id' => 'opt-text-subsection-colors',
    'type' => 'customColor',
    'label' => __('Primary Color'),
    'shared' => true,
    'attributes' => [
        'name' => 'primary_color',
        'value' => '#AF0F26',
    ],
])

Marking an Entire Section as Shared ​

All fields in a shared section inherit the shared behavior automatically.

Object-Oriented Approach ​

php
ThemeOption::setSection(
    ThemeOptionSection::make('opt-text-subsection-colors')
        ->title(__('Colors'))
        ->icon('ti ti-palette')
        ->shared()
        ->fields([
            ColorField::make()
                ->name('primary_color')
                ->label(__('Primary Color'))
                ->defaultValue('#AF0F26'),
            ColorField::make()
                ->name('secondary_color')
                ->label(__('Secondary Color'))
                ->defaultValue('#0989FF'),
        ])
);

Array-Based Approach ​

php
->setSection([
    'title' => __('Colors'),
    'id' => 'opt-text-subsection-colors',
    'icon' => 'ti ti-palette',
    'shared' => true,
    'fields' => [
        // All fields here are shared automatically
    ],
])

Admin UI Behavior ​

When a field is marked as shared:

  • An "All languages" badge appears next to the field label.
  • When editing a non-default language, shared fields are visually disabled with a notice indicating they can only be changed from the default language.
  • Shared fields always store their value at the default key (e.g., theme-shofy-primary_color) regardless of which language is currently being edited.

Filter Hook ​

You can override the shared status of any field using the theme_option_field_is_shared filter:

php
add_filter('theme_option_field_is_shared', function (bool $isShared, string $key): bool {
    // Force a specific field to be shared
    if ($key === 'my_field') {
        return true;
    }

    return $isShared;
}, 20, 2);

Cleanup Command ​

If you previously had locale-specific copies of fields that are now shared, you can clean up the duplicate settings entries:

bash
php artisan cms:theme-option:cleanup-shared

This command removes locale-specific keys for fields that are marked as shared, keeping only the default-language value.