Creating custom PowerToys Run plugins
PowerToys Run is a quick launcher for Windows. It is open-source and modular for additional plugins.
Official plugins include:
- Calculator
- Unit Converter
- Value Generator
- Windows Search
At the time of writing, there are 20 plugins out of the box.
If you think the official plugins are not enough, you can write our own. The easiest way to get started is to look at what others did.
- Official plugins:
- GitHub topic with potentially interesting repos:
Browsing through some of the GitHub repos found above, gives you an idea of how the source code of a plugin looks like.
Contents
- Demo Plugin
- Project
- Metadata
- Main
- Interfaces
- Classes
- Actions
- Logging
- Dependencies
- Tests
- Distribution
- Linting
- Resources
Demo Plugin
As a demo, I created a simple plugin that counts the words and characters of the query.

- ActionKeyword:
demo
Settings:

- Count spaces:
true|false
The source code:
Throughout this blog post, the demo plugin will be used as an example.
Project
Before you create your own project, first take a look at the official checklist:
Key takeaways from the checklist:
- Project name:
Community.PowerToys.Run.Plugin.<PluginName> - Target framework:
net8.0-windows - Create a
Main.csclass - Create a
plugin.jsonfile
In Visual Studio, create a new Class Library project.
The edit the .csproj file to look something like this:
- Platforms:
x64andARM64 UseWPFto include references to WPF assemblies- Dependencies: PowerToys and Wox
.dllassemblies
The .dll files referenced in the .csproj file are examples of dependencies needed, depending on what features your plugin should support.
Unfortunately, there are no official NuGet packages for these assemblies.
Traditionally, plugin authors commit these .dll files to the repo in a libs folder.
Both the x64 and ARM64 versions.
You can copy the DLLs for your platform architecture from the installation location:
C:\Program Files\PowerToys\- Machine wide installation of PowerToys
%LocalAppData%\PowerToys\- Per user installation of PowerToys
You can build the DLLs for the other platform architecture from source:
Other plugin authors like to resolve the dependencies by referencing the PowerToys projects directly. Like the approach by Lin Yu-Chieh (Victor):
I have created a NuGet package that simplifies referencing all PowerToys Run plugin dependencies:
When using Community.PowerToys.Run.Plugin.Dependencies the .csproj file can look like this:
I have also created dotnet new templates that simplifies creating PowerToys Run plugin projects and solutions:

Anyway, it doesn’t matter if you create a project via templates or manually. The project should start out with these files:
Images\*.png- Typically dark and light versions of icons
Main.cs- The starting point of the plugin logic
plugin.json- The plugin metadata
Metadata
Create a plugin.json file that looks something like this:
The format is described in the Dev Documentation:
Main
Create a Main.cs file that looks something like this:
The Main class must have a public, static string property named PluginID:
public static string PluginID => "AE953C974C2241878F282EA18A7769E4";
- 32 digits
Guidwithout hyphens - Must match the value in the
plugin.jsonfile
In addition, the Main class should implement a few interfaces.
Let’s break down the implemented interfaces and the classes used in the example above.
Interfaces
Some interfaces of interest from the Wox.Plugin assembly:
IPluginIPluginI18nIDelayedExecutionPluginIContextMenuISettingProvider
IPlugin
The most important interface is IPlugin:
public interface IPlugin
{
List<Result> Query(Query query);
void Init(PluginInitContext context);
string Name { get; }
string Description { get; }
}
Queryis the method that does the actual logic in the pluginInitis used to initialize the plugin- Save a reference to the
PluginInitContextfor later use
- Save a reference to the
Nameought to match the value in theplugin.jsonfile, but can be localized
IPluginI18n
If you want to support internationalization you can implement the IPluginI18n interface:
public interface IPluginI18n
{
string GetTranslatedPluginTitle();
string GetTranslatedPluginDescription();
}
IDelayedExecutionPlugin
The IDelayedExecutionPlugin interface provides an alternative Query method:
public interface IDelayedExecutionPlugin
{
List<Result> Query(Query query, bool delayedExecution);
}
The delayed execution can be used for queries that take some time to run.
PowerToys Run will add a slight delay before the Query method is invoked, so that the user has some extra milliseconds to finish typing that command.
A delay can be useful for queries that performs:
- I/O operations
- HTTP requests
IContextMenu
The IContextMenu interface is used to add context menu buttons to the query results:
public interface IContextMenu
{
List<ContextMenuResult> LoadContextMenus(Result selectedResult);
}
- Every
Resultcan be enhanced with custom buttons
ISettingProvider
If the plugin is sophisticated enough to have custom settings, implement the ISettingProvider interface:
public interface ISettingProvider
{
Control CreateSettingPanel();
void UpdateSettings(PowerLauncherPluginSettings settings);
IEnumerable<PluginAdditionalOption> AdditionalOptions { get; }
}
CreateSettingPanelusually throw aNotImplementedExceptionUpdateSettingsis invoked when the user updates the settings in the PowerToys GUI- Use this method to save the custom settings and update the state of the plugin
AdditionalOptionsis invoked when the PowerToys GUI displays the settings- Use this property to define how the custom settings are renderer in the PowerToys GUI
Classes
Some classes of interest from the Wox.Plugin assembly:
PluginInitContextQueryResultContextMenuResult
PluginInitContext
A PluginInitContext instance is passed as argument to the Init method:
public class PluginInitContext
{
public PluginMetadata CurrentPluginMetadata { get; internal set; }
public IPublicAPI API { get; set; }
}
PluginMetadatacan be useful if you need the path to thePluginDirectoryor theActionKeywordof the pluginIPublicAPIis mainly used toGetCurrentTheme, but can alsoShowMsg,ShowNotificationorChangeQuery
Query
A Query instance is passed to the Query methods defined in the IPlugin and IDelayedExecutionPlugin interfaces.
Properties of interest:
Searchreturns what the user has searched for, excluding the action keyword.Termsreturns the search as a collection of substrings, split by space (" ")
Result
A list of Result objects are returned by the Query methods defined in the IPlugin and IDelayedExecutionPlugin interfaces.
Example of how to create a new result:
new Result
{
QueryTextDisplay = query.Search, // displayed where the user types queries
IcoPath = IconPath, // displayed on the left side
Title = "A title displayed in the top of the result",
SubTitle = "A subtitle displayed under the main title",
ToolTipData = new ToolTipData("A tooltip title", "A tooltip text\nthat can have\nmultiple lines"),
Action = _ =>
{
Log.Debug("The actual action of the result when pressing Enter.", GetType());
/*
For example:
- Copy something to the clipboard
- Open a URL in a browser
*/
},
Score = 1, // the higher, the better query match
ContextData = someObject, // used together with the IContextMenu interface
}
ContextMenuResult
A list of ContextMenuResult objects are returned by the LoadContextMenus method defined in the IContextMenu interface.
These objects are rendered as small buttons, displayed on the right side of the query result.
Example of how to create a new context menu result:
new ContextMenuResult
{
PluginName = Name,
Title = "A title displayed as a tooltip",
FontFamily = "Segoe Fluent Icons,Segoe MDL2 Assets",
Glyph = "\xE8C8", // Copy
AcceleratorKey = Key.C,
AcceleratorModifiers = ModifierKeys.Control,
Action = _ =>
{
Log.Debug("The actual action of the context menu result, when clicking the button or pressing the keyboard shortcut.", GetType());
/*
For example:
- Copy something to the clipboard
- Open a URL in a browser
*/
},
}
Find the perfect Glyph to use from:
Actions
Examples of actions to use with Result or ContextMenuResult:
Action = _ =>
{
System.Windows.Clipboard.SetText("Some text to copy to the clipboard");
return true;
}
Action = _ =>
{
var url = "https://conductofcode.io/";
if (!Helper.OpenCommandInShell(DefaultBrowserInfo.Path, DefaultBrowserInfo.ArgumentsPattern, url))
{
Log.Error("Open default browser failed.", GetType());
Context?.API.ShowMsg($"Plugin: {Name}", "Open default browser failed.");
return false;
}
return true;
}
Logging
Logging is done with the static Log class, from the Wox.Plugin.Logger namespace.
Under the hood, NLog is used.
Five log levels:
Log.Debug("A debug message", GetType());
Log.Info("An information message", GetType());
Log.Warn("A warning message", GetType());
Log.Error("An error message", GetType());
Log.Exception("An exceptional message", new Exception(), GetType());
The logs are written to .txt files, rolled by date, at:
%LocalAppData%\Microsoft\PowerToys\PowerToys Run\Logs\<Version>\
Dependencies
If you have the need to add third party dependencies, take a look at what is already used by PowerToys.
NuGet packages and the versions specified in the .props file are candidates to reference in your own .csproj file.
Packages of interest:
LazyCacheSystem.Text.Json
If the plugin uses any third party dependencies that are not referenced by PowerToys Run, you need to enable DynamicLoading.
In the plugin.json file:
{
// ...
"DynamicLoading": true
}
truemakes PowerToys Run dynamically load any.dllfiles in the plugin folder
Tests
You can write unit tests for your plugin.
The official plugins use the MSTest framework and Moq for mocking.
- Project name:
Community.PowerToys.Run.Plugin.<PluginName>.UnitTests - Target framework:
net8.0-windows
The .csproj file of a unit test project may look something like this:
- Apart from the actual test assemblies, some package references are also needed
- As well as references to PowerToys and Wox
.dllassemblies
Unit tests of the Main class may look something like this:
Some of the official plugins have unit test coverage:
Distribution
Unfortunately, the plugin manager in PowerToys Run does not offer support for downloading new plugins.
Community plugins are traditionally packaged in .zip files and distributed via releases in GitHub repositories.
The process is described in a unofficial checklist:
The Everything plugin by Lin Yu-Chieh (Victor) is next level and is distributed via:
- Self-Extraction Installer (EXE)
- Manual Installation (ZIP)
- WinGet
- Chocolatey
Linting
I have created a linter for PowerToys Run community plugins:

When running the linter on your plugin any issues are reported with a code and a description. If nothing shows up, your plugin is awesome. The lint rules are codified from the guidelines in the Community plugin checklist.
Resources
Demo Plugin:
Awesome PowerToys Run Plugins:
Third-Party plugins for PowerToy Run:
dotnet new templates for community plugins:
Documentation:
Dev Documentation:
If you want to look under the hood, fork or clone the PowerToys repo:
Get the solution to build on your machine with the help of the documentation: