Use module imports to replace previously symlinked themes, shortcodes, and partials to the parent project

Continuing the discussion from How to use module mount for themes as symlinks no longer work in Hugo 0.123+:

Thanks, I configured the theme as a module as suggested in the previous discussion, and it seems to work just fine. This includes deleting the themes symlink in the child theme, which previously pointed to the parent project; now with theme configured as the module from parent, hugo serve works just fine.

However, this was before deleting other symlinks in the child project. After those were removed, things stopped working.

Now, it isn’t clear how to combine everything correctly in the config.yaml file? Currently, I have:

baseURL: https://www.child-example.com/
languageCode: en-us
title: Site Description

module:
  imports:
    - path: "/Users/user/Git/parent-example.com/themes/MyTheme"

Previously symlinks in the child project were:

layouts/docs/list.html
layouts/shortcodes/badge.html
layouts/_default/_markup/render-image.html
layouts/partials/header.html
assets/css/extended/custom.css
assets/js/extended/footer.js

… which were pointing to:

../parent-example.com/layouts/docs/list.html
.. and etc

Does anyone know if there is a way to make such a configuration work?

Use module mounts instead of symbolic links. See:

Thanks, Joe!

Even though I have read your documentation before, as well as most posts on GitHub and a few of your posts on the forum, it was still very hard to figure things out. I must say that only through luck (reading this post of yours, where you mentioned “But remember to include the default mount”) and hours spent did I figure out how to do this. Such a simple thing should be easy! But it isn’t, due to the lack of real-life examples in the documentation. Replacing the project’s symlinks isn’t straightforward, as many things are implied.

Anyway, I will share how it worked for us, and I hope that it was done correctly and that it will help someone else save hours of time. We also very much hope that it won’t stop working in the future.


We have two projects, for which one project (parent) was sharing things with the other project (child) using symlinks. It was done this way to share code and easily fix bugs across two similar projects.

We use the same theme across both projects, on top of which we build our own modifications, using custom.css file in the parent project and custom_.css. There are tons of other files were linked across the parent and child projects using symlinks.

This is how we ended up making it work considering that the child and parent are in the same directory, i.e.:

/home/ilia/Git/parent-example.com
/home/ilia/Git/child-example.com

The symlinks were replaced as follows:


module:
  mounts:
  - source: layouts
    target: layouts
  - source: ../parent-example.com/layouts/docs/list.html
    target: layouts/docs/single.html
  - source: ../parent-example.com/layouts/docs/list.html
    target: layouts/docs/list.html
  - source: ../parent-example.com/layouts/shortcodes/badge.html
    target: layouts/shortcodes/badge.html
  - source: ../parent-example.com/layouts/shortcodes/link.html
    target: layouts/shortcodes/link.html
  - source: ../parent-example.com/layouts/shortcodes/hotkey.html
    target: layouts/shortcodes/hotkey.html
  - source: ../parent-example.com/layouts/partials/main-buttons.html
    target: layouts/partials/main-buttons.html
  - source: assets
    target: assets
  - source: ../parent-example.com/assets/css/extended/custom.css
    target: assets/css/extended/custom.css
  - source: ../parent-example.com/assets/js/extended/docs-filetree.js
    target: assets/js/extended/docs-filetree.js
  - source: ../parent-example.com/assets/js/extended/docs-toc.js
    target: assets/js/extended/docs-toc.js
  - source: ../parent-example.com/assets/js/extended/header.js
    target: assets/js/extended/header.js
  - source: ../parent-example.com/assets/js/extended/footer.js
    target: assets/js/extended/footer.js
  imports:
  - path: "../../parent-example.com/themes/MyTheme"
    mounts:
    - source: assets
      target: assets
    - source: i18n
      target: i18n
    - source: layouts
      target: layouts

What was hard to figure out?

  1. First of all it’s implied that we needed to do:
  - source: assets
    target: assets

and

  - source: layouts
    target: layouts

… otherwise, nothing would work as expected. The logic for symlinks is different, and guessing those two extra steps in particular wasn’t straightforward.

… furthermore it isn’t clear if we have to use:

mounts:
    - source: assets
      target: assets
    - source: i18n
      target: i18n
    - source: layouts
      target: layouts

For the theme part or just putting:

imports:
  - path: "../../parent-example.com/themes/MyTheme"

… is enough? It seems that it works just fine without it? But why?

Looking at your setup, my general remark would be that you would get a setup that’s much easier to reason about if you (in general) mount directories and not single files. It’s not like you need to use all the files you mount in assets, and if you really want to filter out unwanted files, you can use the includeFiles and/or excludeFiles option on each mount:

Thank you for your suggestion and time!

I remember trying it as the first and most obvious solution, but it didn’t work as expected. Things were completely broken for me; for instance, layouts/partials/nav_menu.html exists in both projects, but those are different files. If you could provide a working example, I’d be happy to try it out.

I think I’ve managed to simplify it! What about this example?

module:
  mounts:
  - source: layouts
    target: layouts
  - source: ../parent-domain.com/layouts/docs
    target: layouts/docs
  - source: ../parent-domain.com/layouts/shortcodes
    target: layouts/shortcodes
  - source: ../parent-domain.com/layouts/_default/_markup
    target: layouts/_default/_markup
  - source: ../parent-domain.com/layouts/partials
    target: layouts/partials
  - source: assets
    target: assets
  - source: ../parent-domain.com/assets/css
    target: assets/css
  - source: ../parent-domain.com/assets/css/extended
    target: assets/css/extended
  - source: ../parent-domain.com/assets/js
    target: assets/js
  - source: ../parent-domain.com/assets/js/extended
    target: assets/js/extended
  imports:
  - path: "../../parent-domain.com/themes/MyTheme"

Is this correct and would work reliably through out future Hugo releases?

However, now the problem is that the footer in the child project is showing the footer of the parent project. This bug doesn’t happen when using files explicitly.

Can anyone explain why? Is there a way to debug it or understand what’s happening?