Vortex API type definitions now ship as the NPM package
@nexusmods/vortex-api, published from the Vortex 2.2 release onward. This repository no longer receives updates.If your extension works and you aren't changing it, you don't need to do anything. A GitHub dependency like
"vortex-api": "Nexus-Mods/vortex-api"will keep resolving against this archived repo.If you're making changes and want the current API, move to the NPM package. Replace the dependency in your
package.json:// before "vortex-api": "Nexus-Mods/vortex-api" // after "@nexusmods/vortex-api": "^2.2.0"Then update your imports to the scoped name:
import { types, util, selectors } from "@nexusmods/vortex-api";If you'd rather not touch imports, you can alias instead with
"vortex-api": "npm:@nexusmods/vortex-api@^2.2.0". Either works, but the scoped name is preferred so your dependencies match your imports. See the Migration Guide for details.
Type definitions for the Vortex extension API.
This package provides auto-generated typings that extensions can use to interact with Vortex - the mod manager by Nexus Mods.
For packaging and distributing extensions, see Packaging extensions for Vortex.
For API changes between versions, see the Changelog.
For a full list of events extensions can listen to or emit, see the Events Reference.
For notable open-source extensions with advanced patterns, see the Example Extensions.
npm install vortex-api
vortex-api declares Vortex runtime packages (React, Redux, Bluebird, etc.) as peerDependencies. With pnpm, npm 7+, or Yarn Berry these are installed automatically - you don't need to add them to your own devDependencies. For details on migrating from earlier versions, see the Migration Guide.
A Vortex extension is a JavaScript module that exports an init function. The function receives an IExtensionContext object that provides access to the full API.
info.json
name- the display name of your extension.author- the extension author's name.version- the version of your extension.description- a short description of what your extension does.
index.js
- This is the main entry point of your extension.
- Import Vortex API types using
import { types, util, selectors } from 'vortex-api' - Must export a
defaultfunction (or namedinit) that receivesIExtensionContext. - Must bundle all external dependencies into the output.
- Extensions - modular plugins that add game support, UI pages, settings, and more. Extensions interact with Vortex through the
IExtensionContextinterface. - State (Redux) - all application state is managed through a Redux store. Extensions can register reducers and react to state changes.
- Profiles - users can create multiple mod profiles per game, each with its own set of enabled mods and configuration.
- Mod Management - handles mod installation, deployment (via symlinks or hardlinks), and conflict resolution.
- Register game support using
registerGame. - Add main pages and dialog pages using
registerMainPageandregisterDialog. - Add mod installers using
registerInstallerandregisterModType. - Add action buttons and toolbar items using
registerAction. - Add settings pages using
registerSettings. - Access the Redux store via
context.api.getState()andcontext.api.store. - Show notifications and dialogs via
context.api.sendNotificationandcontext.api.showDialog.
Extensions can hook into the application lifecycle:
function init(context: IExtensionContext) {
// Called when the extension is loaded.
// Register your features here.
context.once(() => {
// Called after all extensions have been loaded.
// Safe to interact with other extensions here.
});
}The extensions/games/ directory in the Vortex repository contains game support extensions that serve as practical examples. They range from simple to complex:
A minimal game extension registers the game and tells Vortex where to find it. Only id, name, executable, requiredFiles, and queryModPath are required - everything else is optional:
function init(context: IExtensionContext) {
context.registerGame({
id: 'mygame',
name: 'My Game',
mergeMods: true,
logo: 'gameart.jpg',
executable: () => 'MyGame.exe',
requiredFiles: ['MyGame.exe'],
queryModPath: () => 'Mods',
// Vortex will auto-discover the game across all supported stores
queryArgs: {
steam: [{ name: 'My Game' }],
gog: [{ id: '1234567890' }],
xbox: [{ id: 'PublisherName.MyGame' }],
epic: [{ id: 'abc123def456' }],
registry: [{ id: 'HKEY_LOCAL_MACHINE:Software\\MyGame:InstallPath' }],
},
// Environment variables set when launching the game executable
environment: {
SteamAPPId: '12345',
},
// Metadata used by Vortex and Nexus Mods integration
details: {
steamAppId: 12345, // numeric Steam app ID for API lookups
gogAppId: '1234567890', // GOG app ID
nexusPageId: 'mygame', // the game's URL slug on nexusmods.com
hashFiles: [ // files used to identify the game version via hashing
'MyGame.exe',
path.join('Data', 'Main.esm'),
],
},
});
return true;
}See: game-skyrimse for a full example with multi-store support, or game-darksouls, game-grimrock for simpler cases.
Extensions can register installers to handle game-specific mod formats. The installer tests whether it can handle an archive, then returns file placement instructions:
context.registerInstaller('rimworld-mod', 50, testSupported, installContent);
async function testSupported(files: string[]) {
const hasManifest = files.find(f => path.basename(f) === 'About.xml') !== undefined;
return { supported: hasManifest, requiredFiles: [] };
}
async function installContent(files: string[]) {
const instructions = files
.filter(f => !f.endsWith(path.sep))
.map(f => ({ type: 'copy', source: f, destination: f }));
return { instructions };
}See: game-rimworld, game-kenshi, game-stardewvalley, game-neverwinter-nights (file-extension-to-destination routing), Cyberpunk 2077 (multi-type detection from a single archive), Elden Ring (5 priority-based installers), Ready Or Not (type-specific test/install pairs)
Games with multiple mod installation targets can register mod types so users can choose where files are deployed:
context.registerModType(
'bg3-loose',
25,
(gameId) => gameId === 'baldursgate3',
() => path.join(modsPath, 'Loose'),
(instructions) => Promise.resolve(isLooseMod(instructions)),
{ name: 'Loose Files' },
);See: game-baldursgate3, game-stardewvalley, game-nomanssky (mod type migration between deprecated and current formats), Oblivion Remastered (6 mod formats with architecture-aware paths)
Games with strict plugin ordering can register a load order system:
context.registerLoadOrder({
gameId: GAME_ID,
deserializeLoadOrder: () => readCurrentOrder(),
serializeLoadOrder: (order) => writeOrder(order),
validate: (order) => checkForErrors(order),
usageInstructions: 'Drag and drop to reorder plugins.',
});See: game-baldursgate3, game-morrowind (load order with validation and collections support), Bannerlord (auto-sort on deploy), Ready Or Not (prefix-based filesystem ordering), Starfield (conditional LOOT integration)
Extensions can bundle tool definitions so users can launch community tools (script extenders, body editors, modding utilities) directly from Vortex:
const tools: ITool[] = [
{
id: 'skse64',
name: 'Skyrim Script Extender 64',
executable: () => 'skse64_loader.exe',
requiredFiles: ['skse64_loader.exe'],
relative: true,
exclusive: true,
defaultPrimary: true,
},
];
context.registerGame({ ..., supportedTools: tools });See: game-skyrimse, game-stardewvalley, game-oblivion
Extensions can react to deployment, installation, and game mode changes:
context.once(() => {
context.api.onAsync('did-deploy', async (profileId, deployment) => {
// Called after mods are deployed - sync load order, write config, etc.
});
context.api.events.on('gamemode-activated', (gameId: string) => {
// Called when the user switches game mode.
});
});For a full list of available events, see the Events Reference.
See: game-baldursgate3, game-stardewvalley, game-witcher3 (extensive event-driven pipeline), Oblivion Remastered (INI merge on will-deploy, Lua processing on did-deploy), Elden Ring (auto-set primary tool on deploy)
Extensions can register merge functions to combine files from multiple mods (e.g. XML config files):
context.registerMerge(
(game, discovery) => canMerge(game, discovery), // test: can this game merge?
(filePath, mergePath) => doMerge(filePath, mergePath), // perform the merge
'merge-id',
);See: game-dragonage (XML merge for AddIns.xml), game-witcher3 (script merger tool integration), Starfield (INI merge system)
A single extension can register multiple game variants:
function init(context: IExtensionContext) {
context.registerGame({ id: 'neverwinter', name: 'Neverwinter Nights', ... });
context.registerGame({ id: 'neverwinteree', name: 'Neverwinter Nights: Enhanced Edition', ... });
return true;
}For a curated list of notable open-source extensions with advanced patterns, see the Example Extensions.
For bugs and feature requests related to the API, please open an issue on the Vortex repository.