SilverStripe Theme Directories

Silverstripe Version:



I have just converted a site to SilverStripe 4.5, but the themes are very confusing. I’ve read the documentation, but it doesn’t enlighten me any. It seems I have to duplicate the theme to make it work.

An example of the issues:

  1. I put theme in my-project/public/_resources/themes/mytheme - errors with theme mytheme not found
  2. I put theme in my-project/theme/mytheme - none of the css works as {$ThemeDir} template variable is my-project/public/_resources/themes/mytheme
  3. put empty folder my-project/theme/mytheme - css gets pulled from my-project/public/_resources/themes/simple
  4. duplicate theme in my-project/public/_resources/themes/mytheme and my-project/theme/mytheme , it works.

So my question is, why does it need to be in both these places, is there a way to have it in one place? Or is it the $ThemeDir variable that’s the issue? Does it need to be something else, as {$ThemeDir} always seems to go to my-project/public/_resources/themes/mytheme

Even when I use Requirements::ThemedCss, it’s like it checks my-project/theme/mytheme for the existence of the file and either erroring or falling back to simple if it’s not there, then pulls it from my-project/theme/mytheme instead

It sounds like you’re using the public folder. That’s optional and definitely good practice, but comes with a bit of technical overhead. The idea is that the public folder is your web root and only files that must be available for public access are stored there. That would include for example your theme CSS, but not your theme templates.

You can store your CSS files directly in the public folder if you want, but many people (myself included) would rather keep all the theme files in one place, outside the web root. So to make that possible, and allow modules to make files available from the public folder too, Silverstripe has a composer vendor-expose command now which either copies or symlinks all files that need to be public in to the web root each time it is run (Symlinking is more convenient but can be a problem in some workflows). It’s run each time you composer update and you can run it manually as well. To tell Silverstripe which files and folders need to be exposed you can do something like this in your composer.json file:

    "extra": {
        "expose": [

So, I would try putting your theme back in my-project/theme/mytheme, adding the expose section to your composer.json file, and running composer vendor-expose, and see if that fixes your problem.

More information about all of that here: