How to Publish an Electron App to Microsoft Store: From MSIX Packaging to Store Submission
At its core, Electron is just an ordinary Win32 desktop application, but the Microsoft Store only recognizes MSIX. This article, drawing from the actual build configuration we used for HagiCode Desktop, breaks down the entire chain from "registering a developer account → creating MSIX packages → submitting to the store" from start to finish. We'll also discuss the pitfalls we encountered along the way—after all, once you've fallen into the traps, they become stories.
Background
You have an Electron application that needs to be distributed to end users on Windows. Beyond the NSIS installer packages and portable versions we've always used, we also wanted it available in the Microsoft Store. The reasons are actually quite practical:
- Trusted Distribution Channel: Apps in the store are signed and reviewed, so users won't be blocked by SmartScreen during installation, and they won't have to face that cold "Unknown publisher" warning.
- Automatic Updates and Commercialization: The store handles updates for you; subscriptions and permanent licenses can be directly integrated.
- Coverage of Built-in Windows 10/11 Entry Points: winget, store search, Start menu recommendations—these entry points are genuinely useful for user acquisition.
However, Electron is ultimately not UWP. To publish to the Microsoft Store, the core task is essentially one thing—repackage Electron's output into an MSIX package that the Microsoft Store recognizes, and then complete the registration and submission process dutifully. It sounds simple, but once you actually get started, there are quite a few pitfalls. We spent significant effort mapping out the entire process to fill in these gaps. Below, we'll break down each step in detail.
About HagiCode
The solution described in this article comes from our practice in the HagiCode project. HagiCode Desktop is an Electron-based desktop application that needs to be distributed to users through three channels: the official website, GitHub Release, and the Microsoft Store. This article explains how we connected the store channel. There's more information about HagiCode at the end—if you're interested, feel free to scroll down.
Analysis: Four Critical Questions to Clarify Before Publishing
Publishing to the Microsoft Store involves four key technical judgments. Once you've thought these through clearly, you won't need to repeatedly backtrack later—after all, no one wants to redo their work.
1. Microsoft Store Only Accepts MSIX / AppX, Not Traditional NSIS/EXE
The Microsoft Store's support for desktop applications (Desktop Bridge) is built on the MSIX format. Traditional NSIS installer packages cannot be submitted directly; they must first be repackaged into MSIX using MakeAppx. Fortunately, Electron Forge provides a @electron-forge/maker-msix maker that can produce MSIX directly during the packaging phase, saving you the hassle of reverse-engineering packaging from an installed directory.
Our project includes this maker:
{
name: '@electron-forge/maker-msix',
platforms: ['win32'],
config: {
appManifest: msixManifestPath,
packageAssets: msixAssetsPath,
logLevel: 'warn',
...(windowsKitPath ? { windowsKitPath } : {}),
...(windowsKitVersion ? { windowsKitVersion } : {}),
...msixSigningConfig,
},
},
The key inputs are essentially two: appManifest (AppxManifest.xml, which defines package identity and capabilities) and packageAssets (store icon assets). If either of these is wrong, everything else, no matter how well done, is wasted effort.
2. Package Identity Must Be Reserved in Partner Center in Advance
The Identity field (Name, Publisher) in the MSIX package cannot be filled in arbitrarily—it must match exactly the application identity reserved in Partner Center. Even one character off will result in rejection. Our reserved identity is stored in forge.store-config.json:
{
"packageIdentity": {
"displayName": "Hagicode",
"publisherDisplayName": "newbe36524",
"publisher": "CN=8B6C8A94-AAE5-4C8B-9202-A29EA42B042F",
"identityName": "newbe36524.Hagicode",
"backgroundColor": "transparent",
"languages": ["en-US", "zh-CN", "zh-TW", "ja-JP", "ko-KR", "de-DE", "fr-FR", "es-ES", "pt-BR", "ru-RU"]
}
}
That publisher string comes from the certificate subject issued by Microsoft after developer account registration and must match character-for-character. The identityName is the package name prefix you reserved. This string must be copied exactly from Partner Center—never type it manually. We'll discuss this again in the "Common Pitfalls" section later.
3. Desktop Applications Must Declare runFullTrust Capability
Electron applications need full file system access, need to spawn child processes, and need to run the Node runtime—these can only be achieved in "full trust" mode. Therefore, the MSIX manifest must honestly declare the runFullTrust capability, otherwise the application will be blocked by the sandbox upon startup, manifesting as various baffling crashes. Our configuration looks like this:
{
"msix": {
"minVersion": "10.0.17763.0",
"maxVersionTested": "10.0.19045.0",
"capabilities": [
"runFullTrust",
"internetClient",
"internetClientServer",
"privateNetworkClientsServer"
]
}
}
runFullTrust is the standard for desktop application publishing. Setting minVersion to 17763 (Windows 10 1809) is because from this version onwards, MSIX has stable support for desktop Win32 applications; set it lower and users can't install it; set it higher and you won't cover those older machines.
4. Store Submission Requires Windows Environment + Microsoft Store CLI
Packaging can be done on cross-platform CI, but store submission (msstore publish) cannot—it must run the Microsoft Store CLI in a Windows environment with Azure AD application credentials configured. This is why the publish_store job in the automation pipeline must run on a windows-latest runner. This is a hard constraint that can't be bypassed, unlike packaging which can be stuffed into a Linux container.
Solution: Eight-Step Process for Complete Store Publishing
Connecting the analysis above, the complete steps to publish an Electron app to the Microsoft Store are roughly as follows.
Step 1: Register a Developer Account
First, go to Partner Center to register a developer account (individual or company) and pay the one-time fee. After the account is activated, you'll receive a Publisher certificate subject string, which looks roughly like this: CN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX. This is the only source for the publisher field later.
Step 2: Reserve Application Identity in the Store
Create a new application in Partner Center and fill in the name you want to reserve. The system will assign you an identityName, which combined with your own Publisher forms the complete package identity. Copy this identity exactly into your local configuration:
// forge.store-config.json
{
"packageIdentity": {
"displayName": "Hagicode",
"publisherDisplayName": "newbe36524",
"publisher": "CN=8B6C8A94-AAE5-4C8B-9202-A29EA42B042F",
"identityName": "newbe36524.Hagicode"
}
}
Step 3: Prepare Store Icon Assets
The Microsoft Store requires a set of PNGs in fixed dimensions: StoreLogo.png, Square44x44Logo.png, Square150x150Logo.png, Wide310x150Logo.png, etc. Our prepare-msix.js script validates whether all these assets are present before packaging:
// Validate required store icon assets, missing even one won't work
const requiredAssets = ['StoreLogo.png', 'Square44x44Logo.png', 'Square150x150Logo.png', 'Wide310x150Logo.png'];
for (const assetName of requiredAssets) {
const assetPath = path.join(paths.generatedAssetsPath, assetName);
if (!fs.existsSync(assetPath)) {
throw new Error(`Missing required MSIX asset after preparation: ${assetPath}`);
}
}
Why do this? Because if one size is missing, MakeAppx won't tell you exactly what's wrong during packaging, and you'll only get rejected during store review—by which time you've already waited several days. Validating in advance is an effective defense.
Step 4: Generate AppxManifest.xml
The manifest needs to include package identity, capabilities, visual assets, and the entry executable. We use an override configuration (forge.store-config.json) to drive prepare-msix.js to generate the manifest, ensuring the identity matches the store. The key sections of the manifest look roughly like this:
<!-- Package identity: must match Partner Center -->
<Identity Name="newbe36524.Hagicode"
Publisher="CN=8B6C8A94-AAE5-4C8B-9202-A29EA42B042F"
Version="1.2.3.0" />
<Applications>
<Application Id="Hagicode" Executable="Hagicode.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements ... />
</Application>
</Applications>
<!-- Capability declaration: runFullTrust is key for desktop apps -->
<Capabilities>
<rescap:Capability Name="runFullTrust" />
<Capability Name="internetClientServer" />
</Capabilities>
Note that line EntryPoint="Windows.FullTrustApplication"—this is a critical marker for desktop applications. Combined with the runFullTrust capability, it allows the app to run with full permissions. Without it, the app is confined to the sandbox, quite frustratingly.
Step 5: Package with maker-msix
The build command is in package.json:
{
"scripts": {
"build:win:store": "npm run generate:store-bindings && node scripts/build-store-package.js"
}
}
It ultimately calls Electron Forge, passing forge.store-config.json as an override configuration. The maker-msix will call the Windows SDK's MakeAppx to output an .msix file. There's a hard constraint here: packaging must be done on Windows (or in a container with Windows SDK), since it depends on MakeAppx—this can't be bypassed.
Step 6: Sign (Can Skip for Store Submission)
This step is easily overlooked—packages submitted to the store are re-signed by Microsoft with their own certificates, so for development self-testing outside "formal submission," you don't need to sign. However, if you want to install and test locally, you need to sign with a trusted certificate, otherwise Windows will refuse installation. Our resolveMsixSigningConfig returns an empty object when no signing materials are configured, letting the process continue:
// Don't sign if no signing materials configured, let the store re-sign uniformly
function resolveMsixSigningConfig() {
if (!process.env.MSIX_CERT_FILE) return {};
return {
signMethod: 'signtool',
certFilePath: process.env.MSIX_CERT_FILE,
certPassword: process.env.MSIX_CERT_PASSWORD,
};
}
Separating the "self-testing signing" and "submitting unsigned" paths is a key practice.
Step 7: Configure Microsoft Store CLI Credentials
Create an Azure AD application in the Azure portal, grant it permissions to access Partner Center, and obtain the following set of credentials:
AZURE_AD_APPLICATION_CLIENT_IDAZURE_AD_APPLICATION_SECRETAZURE_AD_TENANT_ID-
SELLER_ID(Seller ID from Partner Center) -
MICROSOFT_STORE_PRODUCT_ID(Product ID of the reserved application)
This step is slightly convoluted, but the documentation for both Azure Portal and Partner Center is quite detailed—just follow it.
Step 8: Submit to Store
In a Windows environment, submit using the Microsoft Store CLI:
# Configure credentials
msstore reconfigure --tenantId $env:AZURE_AD_TENANT_ID `
--clientId $env:AZURE_AD_APPLICATION_CLIENT_ID `
--clientSecret $env:AZURE_AD_APPLICATION_SECRET `
--sellerId $env:SELLER_ID
# Submit MSIX package to the reserved product
msstore publish "$packagePath" -id $env:MICROSOFT_STORE_PRODUCT_ID
After submission, you need to return to Partner Center to fill in the store listing details (description, screenshots, pricing, rating), then click submit for review. Review typically takes 1–3 business days; first submissions tend to take longer.
Practice: Consolidating Configuration and Pitfall Experience
After going through the process once, these practices can help you avoid some detours—after all, after you've taken enough detours, they don't feel like detours anymore, but some things can still be skipped.
Store Configuration Files Separately
Separating "general build configuration" from "store-specific configuration" is key. Our approach is: forge.config.js handles daily builds (NSIS, portable, macOS dmg), while forge.store-config.json only extends and overrides during store builds:
{
"extends": "forge.config.js",
"buildVersion": "0.1.0.0",
"packageIdentity": { /* store reserved identity */ },
"msix": {
"minVersion": "10.0.17763.0",
"maxVersionTested": "10.0.19045.0",
"capabilities": ["runFullTrust", "internetClient", "internetClientServer", "privateNetworkClientsServer"]
}
}
This way, store builds and release builds don't pollute each other. HagiCode Desktop maintains three distribution channels simultaneously; configuration separation is the prerequisite for our stable iteration.
Version Numbers Must Be Four Segments
MSIX version numbers must be four segments (Major.Minor.Build.Revision, e.g., 1.2.3.0), but Electron's package.json typically only has three segments. That buildVersion field is used to fill in the last segment—when submitting to the store, version numbers must increment, and the fourth segment is very convenient for distinguishing multiple submissions under the same semantic version. Those who've encountered this will understand; those who haven't will eventually.
Multi-Language Declaration
The store supports multi-language listings, which in the manifest corresponds to each <Resource Language="..." /> tag. We declared ten languages, and the store requires filling in a description for each (you can use machine translation to pass review first, then localize gradually). The corresponding rendering logic in prepare-msix.js is:
// Render language list as Resource tags in MSIX manifest
function renderResourceTags(languages) {
return languages
.map((language) => ` <Resource Language="${escapeXml(language)}" />`)
.join('\n');
}
Common Pitfalls (Focus Here)
HagiCode Desktop has encountered almost every one of these pitfalls:
- Publisher Mismatch: When copying the publisher string from Partner Center, it's easy to accidentally lose a space or mess up the case, leading to immediate rejection.建议直接写成配置文件,别手敲。
-
Missing
runFullTrust: After the app starts, it can't access the file system or spawn child processes, manifesting as various bizarre crashes that are frustrating to debug. -
Incomplete Icon Sizes: MakeAppx doesn't validate, but store review will reject it.
prepare-msix.jsvalidating in advance is an effective defense. - Non-Incrementing Version Numbers: The store refuses to accept the same or lower version numbers; CI pipelines must ensure bumping on each build.
-
Running maker-msix in Non-Windows Environments: It won't find
MakeAppx; you must use awindows-latestrunner. - Signing Confusion: Use self-signed certificates for self-testing, submit unsigned for store submission and let Microsoft re-sign—separate these two paths, don't stuff self-signed certificates into submission packages.
Automation Recommendations
After manually going through the entire process once and understanding each step, it's strongly recommended to connect GitHub Actions for automation. We eventually chained version parsing, MSIX building, GitHub Release publishing, and store publishing into a pipeline, checking for new versions every 4 hours. These details are fully broken down in our other article "Automating Windows App Publication to Microsoft Store."
If you just want to get your app on the store first and integrate commercialization (subscriptions / permanent licenses) later, you can also check out our "How to Integrate Microsoft Store Subscriptions and Permanent Licenses in Electron Desktop Apps"—that article covers connecting commercialization capabilities after store publication.
References
- Microsoft Store CLI Documentation
- electron-forge maker-msix
- MSIX Documentation
- HagiCode Official Site
- HagiCode-org/site GitHub Repository
Summary
Regarding "How to Publish an Electron App to Microsoft Store: From MSIX Packaging to Store Submission," a more prudent approach is to first run through key configurations, dependency boundaries, and implementation paths step by step, then fill in optimization details.
Once objectives, steps, and acceptance criteria are clear, such solutions can typically enter actual delivery more smoothly.
Original Article & License
Thanks for reading. If this article helped, consider liking, bookmarking, or sharing it.
This article was created with AI assistance and reviewed by the author before publication.
- Author: newbe36524
- Original URL: https://docs.hagicode.com/go?platform=devto&target=%2Fblog%2F2026-06-18-electron-app-publish-to-microsoft-store%2F
- License: Unless otherwise stated, this article is licensed under CC BY-NC-SA. Please retain attribution when sharing.
Top comments (0)