Skip to main content
A theme is a folder. Drop it in resources/js/pages/themes/, pick it in the admin settings, and your versions of the pages take over. You never have to copy the whole UI.
This page covers the client theme system, the one that restyles the dashboard, market, tickets and auth screens. It is a different thing from portal themes, which change the public landing page and are managed as database records under Customization. See Portal themes vs client themes.

How it works

1

You create a folder

resources/js/pages/themes/aurora/. The folder name is the theme name.
2

The admin settings page finds it

DezerX scans the themes directory on every load, so there is nothing to register.
3

An admin activates it

That writes the active_theme setting, which is shared with the frontend on every request.
4

Inertia resolves your file first

For each page it tries your theme, then falls back to default.
Resolution order
A theme with one file is valid. quanta ships five auth pages and nothing else, nodepeak ships only a navbar and a footer, and every other screen quietly uses the default.

Create one

1

Make the folder

Use lowercase with hyphens. The admin label is derived from the folder name, so aurora shows as “Aurora” and neon-dark shows as “Neon Dark”.
2

Copy a page you want to change

Starting from the default copy means you inherit the props, the form wiring and the translation keys, and you only have to change markup.
3

Build and activate

Then go to Admin, Settings, Appearance, Client Theme and pick Aurora.
Vite discovers theme files with a glob at build time, so a new folder needs a rebuild (or a restart of npm run dev) before it appears. Adding files to a theme that already exists is picked up by the dev server automatically.
A theme is frontend only, so there’s no Octane reload to do. The workers notice the new build on their own, and switching themes in the admin panel takes effect on the next page load, cached guest pages included.

What you can override

Live in {theme}/auth/. The filename must match exactly.
Admin pages are not themeable. They live in resources/js/pages/admin/ and are shared by every theme.

Layouts come for free

A UI/ page does not need to declare a layout. If yours does not set one, the resolver wraps it in AuthenticatedLayout, which gives you the sidebar, navbar and footer.
resources/js/pages/themes/aurora/UI/dashboard.tsx
If you want a page to render without the standard chrome, set a layout explicitly:
Auth pages are never wrapped. They own their whole screen, which is why quanta can restyle sign in completely without touching anything else.

Overriding components

Copying pages gets expensive if all you want is a different card or button. Most shared pieces in the default theme are wrapped in withThemeOverride, so a file with a matching path replaces them everywhere.
1

Create the file at the same path

2

Export the same name

A default export works too. The named export is tried first.
Your override receives the same props as the original, and callers still pass them. Read the default implementation before you replace it, or you will silently drop behaviour like loading states or permission checks.
Path is relative to {theme}/components/. Export name matters.
Overrides are only consulted when active_theme is not default, and the loader deliberately excludes themes/default/components/** from the on-demand bundle.That keeps the default path free of extra work, and means a theme’s component files are only fetched when that theme is active.
Overriding Footer on a Starter or Cloud licence. Those licences require the DezerX watermark, and the licence check reads the active theme’s Footer.tsx. Copy the watermark block from themes/default/components/Footer.tsx into your footer unchanged, or the site is put into maintenance mode until it’s back. Professional, Ultimate and Cloud+ licences don’t need it.

Colours and spacing

Before writing custom CSS, reach for the existing variables. They are what the admin Colors page edits, so anything built on them stays in sync when an operator retunes the palette.
Button helpers defined in app.css: button-primary, button-secondary, button-success, button-warning, button-danger. Use Tailwind’s rounded-* classes for corners rather than fixed pixel values. The radius scale follows the Corner Roundness setting in Admin, Settings, so rounded-xl gets rounder or squarer along with the rest of the UI.
Counting usage across the default theme: text-primary, border-primary, text-secondary, bg-secondary and bg-primary do most of the work. Match those and your theme will respond to light and dark mode and to operator colour changes without extra effort.
Hardcoding hex values is the most common theme mistake. It looks fine in the mode you built in, then breaks in the other one and ignores whatever the operator sets under Customization, Colors.

What your pages receive

Theme pages are ordinary Inertia pages, so props come from the controller that rendered them. Two things are always available.
settings is an array of every non-sensitive row from the settings table, which is also where active_theme itself lives.
Copy the default page you are replacing and keep its interface PageProps. That is the quickest way to see exactly what the controller sends without reading the PHP.

Things worth reusing

You are not limited to your own folder. These are shared and safe to import from any theme.
Run any HTML or SVG that came from a database setting through @/lib/sanitize before rendering it. Operators can paste custom markup into settings, and a theme that renders it raw is an XSS hole.

Test it

1

Activate the theme

Admin, Settings, Appearance, Client Theme.
2

Walk every page you overrode

Plus one you did not, to confirm the fallback still works.
3

Toggle light and dark mode

Then change a colour under Customization, Colors and reload.
4

Check narrow screens

The default components are responsive. Overrides often are not.
5

Sign out and back in

Auth pages render without a session, so a prop you assumed exists may be null.
6

Switch back to Default

Confirm nothing you changed leaked into the default theme.

Keeping your theme through updates

Updates replace the application directory. With Keep custom dashboard themes ticked, which is the default (--keep-themes in non-interactive mode), the updater saves your theme folders, puts them back after the new release is unpacked, and only then runs npm run build, so they’re in the new bundle without you doing anything.
A folder with the same name as a theme that ships with DezerX (default, quanta, nodepeak) is replaced by the release’s version, and your changes to it are lost. Never edit a bundled theme in place: copy it to a new folder name and change the copy.
Keep the theme folder in version control as well. It’s a single directory, so restoring it by hand is a copy and an npm run build.

Portal themes vs client themes

Two separate systems with similar names.

Client themes

This page. Folders in resources/js/pages/themes/, selected with the active_theme setting. Restyle the dashboard, market, tickets and auth screens.

Portal themes

Database records (portal_themes) managed under Admin, Customization. Each points at a component path such as resources/js/pages/Portal/Default/welcome.tsx and changes the public landing page.
Activating one has no effect on the other.

When it does not work