Documentation Index Fetch the complete documentation index at: https://mintlify.com/modrinth/code/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Modrinth App is a desktop application for managing Minecraft mods, modpacks, and instances. Built with Tauri 2.x, it combines a Rust backend (Theseus) with a Vue 3 frontend.
Platforms : Windows, macOS, Linux
Framework : Tauri 2.x
Backend : Rust (Theseus library)
Frontend : Vue 3 + Tailwind CSS
Architecture
Tauri Application Model
┌───────────────────────────────────────────────┐
│ Tauri Application Window │
├───────────────────────────────────────────────┤
│ │
│ WebView (Vue 3 Frontend) │
│ apps/app-frontend/ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Vue Components, Router, etc. │ │
│ └──────────────────────────────────┘ │
│ │ │
│ │ IPC (Tauri Commands) │
│ ↓ │
│ ┌──────────────────────────────────┐ │
│ │ Rust Backend (Theseus) │ │
│ │ packages/app-lib/ │ │
│ │ │ │
│ │ • Profile Management │ │
│ │ • Mod Installation │ │
│ │ • Game Launching │ │
│ │ • File System Access │ │
│ │ • SQLite Database │ │
│ └──────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────┘
│ │
↓ ↓
Native System APIs Modrinth APIs
(FS, Process, etc.) (Labrinth, etc.)
Component Separation
Tauri Shell (apps/app/) - Desktop framework, native integrations
Frontend (apps/app-frontend/) - Vue 3 UI rendered in WebView
Backend Library (packages/app-lib/) - Theseus Rust library for core logic
Directory Structure
App Shell (apps/app/)
apps/app/
├── src/
│ ├── main.rs # Tauri app entry point
│ └── lib.rs # Tauri command exports
├── src-tauri/
│ └── tauri.conf.json # Tauri configuration
├── Cargo.toml # Rust dependencies
└── package.json # Frontend build integration
Frontend (apps/app-frontend/)
apps/app-frontend/
├── src/
│ ├── App.vue # Root component
│ ├── main.ts # Vue app entry
│ ├── router.ts # Vue Router
│ ├── pages/ # Page components
│ │ ├── Browse.vue # Browse mods/modpacks
│ │ ├── Library.vue # User's instances
│ │ ├── Instance.vue # Instance detail view
│ │ └── Settings.vue # App settings
│ ├── components/ # App-specific components
│ └── helpers/ # Utilities
├── index.html
├── vite.config.ts
└── package.json
Backend Library (packages/app-lib/)
packages/app-lib/ (Theseus)
├── src/
│ ├── lib.rs # Library entry point
│ ├── api/ # Tauri command handlers
│ │ ├── profile.rs # Profile management
│ │ ├── pack.rs # Modpack operations
│ │ ├── metadata.rs # Minecraft metadata
│ │ └── ...
│ ├── profile/ # Profile & instance logic
│ ├── launcher/ # Game launching
│ ├── pack/ # Modpack import/export
│ ├── state/ # Application state
│ ├── util/ # Utilities
│ └── event/ # Event emitters
├── migrations/ # SQLite migrations
├── java/ # Java runtime detection
├── Cargo.toml
└── .env.local # Environment template
Tauri Commands (IPC)
The frontend communicates with the backend via Tauri commands.
Defining Commands (Rust)
packages/app-lib/src/api/profile.rs
use tauri :: State ;
use crate :: state :: AppState ;
#[tauri :: command]
pub async fn get_all_profiles (
state : State <' _ , AppState >,
) -> Result < Vec < Profile >, String > {
let profiles = state . profiles . get_all () . await
. map_err ( | e | e . to_string ()) ? ;
Ok ( profiles )
}
#[tauri :: command]
pub async fn create_profile (
name : String ,
game_version : String ,
mod_loader : String ,
state : State <' _ , AppState >,
) -> Result < Profile , String > {
let profile = state . profiles . create (
& name ,
& game_version ,
& mod_loader ,
) . await . map_err ( | e | e . to_string ()) ? ;
Ok ( profile )
}
Registering Commands
use tauri :: Builder ;
fn main () {
Builder :: default ()
. invoke_handler ( tauri :: generate_handler! [
get_all_profiles ,
create_profile ,
install_mod ,
launch_instance ,
// ... more commands
])
. run ( tauri :: generate_context! ())
. expect ( "error while running tauri application" );
}
Calling from Frontend (TypeScript)
apps/app-frontend/src/helpers/profile.ts
import { invoke } from '@tauri-apps/api/core'
export interface Profile {
id : string
name : string
gameVersion : string
modLoader : string
// ...
}
export async function getAllProfiles () : Promise < Profile []> {
return await invoke < Profile []>( 'get_all_profiles' )
}
export async function createProfile (
name : string ,
gameVersion : string ,
modLoader : string ,
) : Promise < Profile > {
return await invoke < Profile >( 'create_profile' , {
name ,
gameVersion ,
modLoader ,
})
}
Usage in Components
< script setup lang = "ts" >
import { ref , onMounted } from 'vue'
import { getAllProfiles , createProfile } from '@/helpers/profile'
const profiles = ref < Profile []>([])
const loading = ref ( true )
onMounted ( async () => {
profiles . value = await getAllProfiles ()
loading . value = false
})
async function handleCreateProfile () {
const newProfile = await createProfile ( 'My Modpack' , '1.20.1' , 'fabric' )
profiles . value . push ( newProfile )
}
</ script >
< template >
< div >
< h1 > My Instances </ h1 >
< div v-if = " loading " > Loading... </ div >
< div v-else >
< ProfileCard
v-for = " profile in profiles "
: key = " profile . id "
: profile = " profile "
/>
</ div >
< button @ click = " handleCreateProfile " > Create New Instance </ button >
</ div >
</ template >
Theseus (Backend Library)
Theseus (packages/app-lib/) is the Rust library powering the app.
Core Features
Profile Management
Profiles represent Minecraft instances (game version + mods + config).
use theseus :: profile :: Profile ;
// Create profile
let profile = Profile :: create (
"My Modpack" ,
"1.20.1" ,
"fabric" ,
) . await ? ;
// Add mod
profile . add_project (
"sodium" ,
"version_id_123" ,
) . await ? ;
// Launch game
let process = profile . launch () . await ? ;
Mod Installation
use theseus :: pack :: install_mod;
// Install from Modrinth
install_mod (
profile_id ,
"sodium" , // Project ID or slug
Some ( "latest" ), // Version (or None for latest compatible)
) . await ? ;
// Install from file
install_mod_from_file (
profile_id ,
PathBuf :: from ( "/path/to/mod.jar" ),
) . await ? ;
Minecraft Version Management
use theseus :: metadata :: Metadata ;
// Get available versions
let versions = Metadata :: get_minecraft_versions () . await ? ;
// Get loader versions (Forge, Fabric, Quilt)
let fabric_versions = Metadata :: get_fabric_versions ( "1.20.1" ) . await ? ;
Java Runtime Detection
use theseus :: launcher :: JavaVersion ;
// Auto-detect Java installations
let java_installs = JavaVersion :: find_all () . await ? ;
// Get or download Java for version
let java = JavaVersion :: get_for_minecraft ( "1.20.1" ) . await ? ;
Game Launching
use theseus :: launcher :: launch;
// Launch instance
let process = launch (
profile_id ,
Some ( java_path ),
Some ( memory_mb ),
) . await ? ;
// Monitor process
process . on_stdout ( | line | {
println! ( "Game: {}" , line );
});
let exit_code = process . wait () . await ? ;
State Management
Theseus maintains application state in memory and SQLite.
use parking_lot :: RwLock ;
use std :: sync :: Arc ;
pub struct AppState {
pub profiles : ProfileManager ,
pub metadata : MetadataCache ,
pub settings : Arc < RwLock < Settings >>,
pub db : SqlitePool ,
}
impl AppState {
pub async fn init () -> Result < Self > {
let db = SqlitePool :: connect ( "modrinth.db" ) . await ? ;
Ok ( Self {
profiles : ProfileManager :: new ( & db ) . await ? ,
metadata : MetadataCache :: new () . await ? ,
settings : Arc :: new ( RwLock :: new ( Settings :: load () ? )),
db ,
})
}
}
Event System
Theseus emits events for long-running operations:
use theseus :: event :: {emit, EventPayload };
// Emit progress event
emit ( EventPayload :: DownloadProgress {
id : "mod_download" ,
current : 1024 ,
total : 5120 ,
});
// Emit completion
emit ( EventPayload :: DownloadComplete {
id : "mod_download" ,
});
Frontend subscribes:
import { listen } from '@tauri-apps/api/event'
listen <{ id : string ; current : number ; total : number }>(
'download-progress' ,
( event ) => {
const { current , total } = event . payload
progress . value = ( current / total ) * 100
}
)
Database (SQLite)
Local data is stored in SQLite:
migrations/001_create_profiles.sql
CREATE TABLE profiles (
id TEXT PRIMARY KEY ,
name TEXT NOT NULL ,
game_version TEXT NOT NULL ,
mod_loader TEXT NOT NULL ,
created_at INTEGER NOT NULL ,
last_played INTEGER
);
CREATE TABLE installed_mods (
profile_id TEXT NOT NULL ,
project_id TEXT NOT NULL ,
version_id TEXT NOT NULL ,
file_name TEXT NOT NULL ,
PRIMARY KEY ( profile_id , project_id ),
FOREIGN KEY ( profile_id ) REFERENCES profiles ( id )
);
Queries:
use sqlx :: { SqlitePool , query_as};
pub async fn get_profile ( id : & str , pool : & SqlitePool ) -> Result < Profile > {
query_as! (
Profile ,
"SELECT * FROM profiles WHERE id = ?" ,
id
)
. fetch_one ( pool )
. await
. map_err ( Into :: into )
}
Frontend (Vue 3)
The frontend is a Vue 3 SPA rendered in Tauri’s WebView.
API Client Integration
Use @modrinth/api-client with TauriModrinthClient:
import { TauriModrinthClient , AuthFeature } from '@modrinth/api-client'
import { provideModrinthClient } from '@modrinth/ui'
import { getVersion } from '@tauri-apps/api/app'
const version = await getVersion ()
const client = new TauriModrinthClient ({
userAgent: `modrinth/theseus/ ${ version } (support@modrinth.com)` ,
features: [
new AuthFeature ({
token : async () => auth . value . token ,
}),
],
})
provideModrinthClient ( client )
Shared Components
The app reuses components from @modrinth/ui:
< script setup >
import { Button , Modal , ProjectCard } from '@modrinth/ui'
</ script >
< template >
< div >
< ProjectCard : project = " project " />
< Button @ click = " install " > Install </ Button >
</ div >
</ template >
See the cross-platform-pages skill for shared page components.
Routing
Vue Router handles navigation:
import { createRouter , createWebHistory } from 'vue-router'
const routes = [
{ path: '/' , component : () => import ( './pages/Browse.vue' ) },
{ path: '/library' , component : () => import ( './pages/Library.vue' ) },
{ path: '/instance/:id' , component : () => import ( './pages/Instance.vue' ) },
{ path: '/settings' , component : () => import ( './pages/Settings.vue' ) },
]
export const router = createRouter ({
history: createWebHistory (),
routes ,
})
Native Features
File System Access
import { open } from '@tauri-apps/plugin-dialog'
import { readTextFile } from '@tauri-apps/plugin-fs'
// File picker
const selected = await open ({
multiple: false ,
filters: [{ name: 'Modpack' , extensions: [ 'mrpack' ] }],
})
// Read file
const content = await readTextFile ( selected . path )
Deep Links
Handle modrinth:// URLs:
use tauri_plugin_deep_link :: DeepLink ;
Builder :: default ()
. plugin ( DeepLink :: new ())
. setup ( | app | {
app . listen_deep_link ( | url | {
// Handle modrinth://install/sodium
if url . starts_with ( "modrinth://install/" ) {
let project_id = url . strip_prefix ( "modrinth://install/" ) . unwrap ();
// Trigger install
}
});
Ok (())
})
System Tray
use tauri :: SystemTray ;
let tray = SystemTray :: new () . with_menu ( /* menu */ );
Builder :: default ()
. system_tray ( tray )
. on_system_tray_event ( | app , event | {
// Handle tray clicks
})
Auto-Updates
use tauri_plugin_updater :: UpdaterExt ;
let updater = app . updater ();
if let Some ( update ) = updater . check () . await ? {
update . download_and_install () . await ? ;
app . restart ();
}
Build Targets
# macOS (x64 + ARM64 universal binary)
cargo tauri build --target universal-apple-darwin
# Windows
cargo tauri build --target x86_64-pc-windows-msvc
# Linux
cargo tauri build --target x86_64-unknown-linux-gnu
CI/CD (GitHub Actions)
The theseus-build.yml workflow builds for all platforms:
jobs :
build :
strategy :
matrix :
platform : [ macos-latest , windows-latest , ubuntu-latest ]
runs-on : ${{ matrix.platform }}
steps :
- uses : actions/checkout@v4
- uses : dtolnay/rust-toolchain@stable
- run : pnpm install
- run : pnpm app:build
- uses : actions/upload-artifact@v4
with :
name : app-${{ matrix.platform }}
path : apps/app/src-tauri/target/release/bundle/
Development
Running Locally
# Install dependencies
pnpm install
# Copy environment file
cd packages/app-lib
cp .env.local .env
# Start development (from root)
pnpm app:dev
This launches the app with:
Frontend hot-reloading (Vite HMR)
Rust recompilation on code changes (requires manual restart)
Environment Variables
packages/app-lib/.env.local
# API endpoints (use staging for development)
LABRINTH_URL = https://staging-api.modrinth.com
# Analytics
ANALYTICS_ENABLED = false
Debugging
Open DevTools :
macOS: Cmd+Option+I
Windows/Linux: Ctrl+Shift+I
Rust logs :
use tracing :: info;
info! ( "Profile created: {}" , profile . id);
Logs appear in the terminal running pnpm app:dev.
Testing
Rust Tests
# Run all tests
cargo test -p theseus
# Run specific test
cargo test -p theseus test_profile_creation
Frontend Tests
pnpm --filter @modrinth/app-frontend run test
Integration Tests
tests/integration_test.rs
#[tokio :: test]
async fn test_install_and_launch () {
let state = AppState :: init () . await . unwrap ();
let profile = create_profile ( "Test" , "1.20.1" , "fabric" , & state ) . await . unwrap ();
install_mod ( & profile . id, "sodium" , None , & state ) . await . unwrap ();
let process = launch ( & profile . id, None , None , & state ) . await . unwrap ();
assert! ( process . is_running ());
}
Pre-PR Checks
# From root directory
pnpm prepr:frontend:app
# Rust checks
cargo clippy -p theseus --all-targets
cargo fmt --check
Distribution
Installers
Tauri creates platform-specific installers:
macOS : .dmg (disk image) + .app bundle
Windows : .msi (installer) + .exe (portable)
Linux : .deb, .AppImage
Download
Users download from modrinth.com/app .
Auto-Updates
The app checks for updates on launch and downloads/installs automatically.
Next Steps
Packages Learn about shared packages (Theseus, UI, etc.)
Testing Testing strategies for Rust and Vue
Deployment CI/CD and release process
Frontend (Web) Compare with the web frontend architecture