My themes are in a Github repo, and built with a github action to create a release zip. I upload those onto our production site after release manually so I can update any content that’s needed.
With this, I’m not actually developing inside the theme folder thats on production ever. When I pull a site down with local, I then also separately checkout my project into another theme directory. I then symbolic link that theme to my local wp site.
This works well, except every time I pull the site to get updated content changes, since it pulls all files it deletes that symbolic link and switches to the production theme (which makes sense). I then have to re symlink it and switch back to my development theme.
This works fine for me, but isn’t very streamlined and leads to confusion for our team.
I’m curious how other people deal with pulling sites where the theme is a built artifact thats stored in github and you don’t edit it directly. I have tried selecting “choose files” when pulling to avoid overriding my symlink, but its very slow to load the change list sometimes.
I’m thinking of making a localwp addon that lets me choose a theme directory and have it automatically relink the theme and activate it on pulls but before I do that I’d like to know if theres a better way.
This adds an external theme row to the overview, which lets you pick a theme folder to symlink. It gives buttons to also recreate the link should it get deleted, and open the theme in the file explorer or vscode directly.
The addon documentation is in a rough state, and the create addon npm tool produces errors because eslint is so out of date. It took a while to get this working. I still have a few issues:
Using github action to bundle the package ends up providing errors about missing node_modules when installing the extension rather than running it in dev mode. The node_modules are all there so not sure whats wrong as it looks the same as the notes addon example.
The siteClone hook doesnt seem to fire to have it auto relink after pulling. The docs don’t mention if these are main or renderer actions but I’m assuming this would be main hook.
Id love to get it to auto reactivate the theme, but Id need a programatic way to open the site shell and run the theme activate wp cli command. Not sure how to do that, so that remains a manual step after pulling.
Thank you for all of the deal and context here so far @joejankowiak!
We’ll admit our add on documentation could use some work at the moment. We’ve had to prioritize other work and some of that has included either folding add ons into Local or discontinuing ones that are no longer necessary.
That said we’re happy to help dig in here a bit more. Could you provide some more details to help us take a further look?
What is your OS/OS version?
What version of Local are you on?
Please attach a copy of your Local Logs from the Download widget in your Local app.
Do you also work with a team of multiple developers or are you the only person who is accessing the site, git files and then utilizing the add on?
Hey Nick, do you guys plan on keeping addons as a feature in the app long term? Totally understand how documentation is one of the first things to go when you get busy.
We have a few developers, but hopefully in the near future expand that even more. I want to make our workflow as dead simple as possible for them, so local addons are a great way to do this!
I’m on Windows 11, but we also have mac developers.
My local version is Version 9.2.9+6887
Heres the relevant part of the log when I try to install my addon.
Summary
{“class”:“RendererAddonLoader”,“level”:“error”,“message”:“Error Loading Add-on: %%userDataPath%%\addons\external-theme\lib\renderer.js”,“stack”:“Error: Cannot find module ‘%%userDataPath%%\addons\external-theme\node_modules\react-lowlight\src\Lowlight.js’. Please verify that the package.json has a valid “main” entry\n at tryPackage (node:internal/modules/cjs/loader:509:19)\n at Module._findPath (node:internal/modules/cjs/loader:771:18)\n at Module._resolveFilename (node:internal/modules/cjs/loader:1376:27)\n at a._resolveFilename (node:electron/js2c/renderer_init:2:2719)\n at r._resolveFilename (file:///C:/Program%20Files%20(x86)/Local/resources/app.asar/renderer/_browserWindows/app/app.js:288:556619)\n at defaultResolveImpl (node:internal/modules/cjs/loader:1032:19)\n at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1037:22)\n at Module._load (node:internal/modules/cjs/loader:1199:37)\n at c._load (node:electron/js2c/node_init:2:17993)\n at s._load (node:electron/js2c/renderer_init:2:31794)”,“thread”:“renderer”,“timestamp”:“2026-02-06T15:15:14.091Z”}
It complains about react-lowlight not being found, but it exists in the node_modules. In the github repo i linked is a built release addon package if anyone wants to try installing it. I gave up on it for the day to focus on other work, but I based it on the folder structure of the local-addon-notes project.
I did submit this as a community addon via the build.localwp.com site. I think its potentially useful to others to be able to develop a theme in an external folder.
Hey @joejankowiak - I’m the product manager on the Local team and work closely with Nick. Thanks for reaching out!
We do not have any plans to deprecate addons at this time, but we are making intentional efforts to move away from them where possible. For example, the next release of Local (coming soon!) will bring Cloud Backups into the app natively, and after a waiting period, we will sunset the Backups addon.
We have not prioritized updating the documentation for building addons, in part because demand is low - though I recognize that is a chicken or the egg problem.
As for your specific error - I’m not sure on the node_modules piece, but I will have engineering take a look.
Change your release.yml to use npm pack. You’re doing npm run build and then taring up the package directory, but that won’t include the bundleDependencies which is why you see the missing modules errors. npm pack creates a tar file, you don’t need to tar separately.
With those changes (I ran npm install && npm pack locally rather than using GitHub Actions) I was able to get your add-on to build and run in Local:
Id love to get it to auto reactivate the theme, but Id need a programatic way to open the site shell and run the theme activate wp cli command. Not sure how to do that, so that remains a manual step after pulling.
We do something similar in the Headless add-on to install and activate plugins via WP-CLI commands. That code is open source and might be helpful as a starting point:
The siteClone hook doesnt seem to fire to have it auto relink after pulling. The docs don’t mention if these are main or renderer actions but I’m assuming this would be main hook.
I’ll look into this separately and get back to you here…
@austinwendt Thanks for the info about the future of addons. Good to know there’s no hard plan to remove them completely. Also awesome news on the cloud backups, I was just about to start using that so great to see it getting baked in!
@nickc Thank you for taking a look at this! I was unaware npm pack did anything extra, it looked like it just also called npm run build. Also thank you for linking the relevant headless addon code, that should get me what I need. I had figured it probably was impossible to get into the sites CLI . Appreciate you taking the time out to help me out!
Instead of the siteCloned hook in your main file, you could try the code below in your renderer.jsx file (untested, but sitePulled is likely more relevant to your requirement to run something after a pull).
diff --git a/src/renderer.jsx b/src/renderer.jsx
index 3e79202..b9447e5 100644
--- a/src/renderer.jsx
+++ b/src/renderer.jsx
@@ -12,6 +12,13 @@ const addonID = packageJSON.slug;
export default function (context) {
const { React, hooks } = context;
+ // Auto-relink theme after pulling from Connect
+ ipcRenderer.on("sitePulled", async (_event, site) => {
+ if (site?.id) {
+ await ipcRenderer.invoke("external-theme-sync-theme", site.id);
+ }
+ });
+
// Include global style sheet
const stylesheetPath = path.resolve(__dirname, "../style.css");
@nickc Got the addon building and that wp cli code to activate the theme is working great!
The sitePulled action doesnt seem to work either, in main or render. Going off of the headless addon, I tried the siteStarted action, and that does fire.
siteStarted does fire on pull if its not already started. This is better than nothing, so I’ll leave that in there. I don’t want to take up too much of your time, so unless you have a hunch for why those arn’t firing (maybe they were renamed?) I can leave it at that.
I tracked this down to sitePulled only firing for Flywheel-hosted pulls, @joejankowiak. WPE-hosted pulls use a different process, so siteStarted is likely your best option here. Sorry that we don’t offer a universal pull hook.
Hey @nickc, thanks for diving into that. Unfortunate but good to know I’m not just doing something wrong. This addon already makes our workflow so much better so I’m happy and thanks again for helping me get it working.