# Using PlutoLinks @ingredients with Julia Modules: Gotcha and Workaround

**URL:** <https://discourse.julialang.org/t/using-plutolinks-ingredients-with-julia-modules-gotcha-and-workaround/135940>\
**Category:** Pluto\
**Created:** [March 1, 2026, 4:11pm UTC](https://discourse.julialang.org/t/using-plutolinks-ingredients-with-julia-modules-gotcha-and-workaround/135940 "2026-03-01T16:11:37Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![gluque](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gluque/32/9665_2.png) [@gluque](https://discourse.julialang.org/u/gluque)\
**Post date:** [March 1, 2026, 4:11pm UTC](https://discourse.julialang.org/t/using-plutolinks-ingredients-with-julia-modules-gotcha-and-workaround/135940/1 "2026-03-01T16:11:37Z")

</div>

While setting up a Pluto notebook that depends on helper functions defined in an external script, I ran into a subtle behavior of `PlutoLinks.@ingredients` that’s worth documenting.

**The goal**

I wanted to keep reusable functions in an external script (`scripts/MyModule.jl`) and load them reactively into a Pluto notebook so that edits to the file automatically trigger re-evaluation of dependent cells, which is the main appeal of `@ingredients`over a plain `include`.

**Basic usage**

```julia-auto
using PlutoLinks: @ingredients
M = @ingredients "../scripts/MyModule.jl"

```

`@ingredients` watches the file for changes and re-runs dependent cells reactively. Great.

**The gotcha: double-nesting when the script defines a `module`**

If your script is a plain Julia file (no `module` wrapper), functions are accessible directly:

```julia-auto
# scripts/MyModule.jl — no module wrapper
function my_func(x)
    x + 1
end

```

In Pluto (these statements should go in different chunks)

```julia-auto
M = @ingredients "../scripts/MyModule.jl"
M.my_func(1) # ✅ works

```

However, if your script wraps its content in a `module`, `@ingredients` wraps the entire script in its own namespace too, resulting in **double-nesting** :

```julia-auto
# scripts/MyModule.jl — with module wrapper
module MyModule
export my_func
function my_func(x)
    x + 1
end
end #module

```

Then, in Pluto:

```julia-auto
M = @ingredients "../scripts/MyModule.jl"
M.my_func(1) # ❌ UndefVarError
M.MyModule.my_func(1) # ✅ works, but awkward

```

Of course you can drop the `module` wrapper in the script and write plain Julia, but IMHO it’s better to keep the `module` (useful if you also `include` the file elsewhere or plan to turn it into a proper package), and alias it in the notebook:

```julia-auto
M = @ingredients "../scripts/MyModule.jl"
const MyModule = M.MyModule
MyModule.my_func(1) # ✅ clean

```

Hope this saves someone an hour of head-scratching!

GL

---

<div class="post-metadata">

**Author:** ![fonsp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fonsp/32/222349_2.png) [@fonsp](https://discourse.julialang.org/u/fonsp)\
**Post date:** [March 2, 2026, 12:04pm UTC](https://discourse.julialang.org/t/using-plutolinks-ingredients-with-julia-modules-gotcha-and-workaround/135940/2 "2026-03-02T12:04:20Z")

</div>

That’s a good tip, thanks for posting!

How do you feel about contributing this tip to the documentation of PlutoLinks? That would make sure that other people also see this information.

---

<div class="post-metadata">

**Author:** ![gluque](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gluque/32/9665_2.png) [@gluque](https://discourse.julialang.org/u/gluque)\
**Post date:** [March 2, 2026, 7:42pm UTC](https://discourse.julialang.org/t/using-plutolinks-ingredients-with-julia-modules-gotcha-and-workaround/135940/3 "2026-03-02T19:42:45Z")

</div>

Thanks for the comments. I just made the pull request: [docs: add Julia Modules section to @ingredients documentation by gluque · Pull Request #18 · JuliaPluto/PlutoLinks.jl · GitHub](https://github.com/JuliaPluto/PlutoLinks.jl/pull/18)
