adding-tauri-splashscreen
Tauri Splashscreen Implementation
This skill covers implementing splash screens in Tauri v2 applications. A splash screen displays during application startup while the main window loads and initializes.
Overview
The splash screen pattern involves:
- Showing a splash window immediately on launch
- Hiding the main window until ready
- Performing initialization tasks (frontend and backend)
- Closing splash and showing main window when complete
Configuration
Window Configuration
Configure both windows in tauri.conf.json:
{
"app": {
"windows": [
{
"label": "main",
"title": "My Application",
"width": 1200,
"height": 800,
"visible": false
},
{
"label": "splashscreen",
"title": "Loading",
"url": "splashscreen.html",
"width": 400,
"height": 300,
"center": true,
"resizable": false,
"decorations": false,
"transparent": true,
"alwaysOnTop": true
}
]
}
}
Key settings:
"visible": falseon main window - hides it until ready"url": "splashscreen.html"- points to splash screen HTML"decorations": false- removes window chrome for cleaner look"transparent": true- enables transparent backgrounds"alwaysOnTop": true- keeps splash visible during loading
Splash Screen HTML
Create splashscreen.html in your frontend source directory (e.g., src/ or public/):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loading</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
overflow: hidden;
background: transparent;
}
.splash-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
color: white;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.logo {
width: 80px;
height: 80px;
margin-bottom: 24px;
}
.app-name {
font-size: 24px;
font-weight: 600;
margin-bottom: 16px;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top-color: #4f46e5;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
margin-top: 16px;
font-size: 14px;
color: rgba(255, 255, 255, 0.7);
}
</style>
</head>
<body>
<div class="splash-container">
<!-- Replace with your logo -->
<svg class="logo" viewBox="0 0 100 100" fill="none">
<circle cx="50" cy="50" r="45" stroke="#4f46e5" stroke-width="4"/>
<path d="M30 50 L45 65 L70 35" stroke="#4f46e5" stroke-width="4" fill="none"/>
</svg>
<div class="app-name">My Application</div>
<div class="loading-spinner"></div>
<div class="loading-text">Loading...</div>
</div>
</body>
</html>
Frontend Setup
TypeScript/JavaScript Implementation
In your main entry file (e.g., src/main.ts):
import { invoke } from '@tauri-apps/api/core';
// Helper function for delays
function sleep(seconds: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}
// Frontend initialization
async function initializeFrontend(): Promise<void> {
// Perform frontend setup tasks here:
// - Load configuration
// - Initialize state management
// - Set up routing
// - Preload critical assets
// Example: simulate initialization time
await sleep(1);
// Notify backend that frontend is ready
await invoke('set_complete', { task: 'frontend' });
}
// Start initialization when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
initializeFrontend().catch(console.error);
});
Alternative: Using Window Events
import { invoke } from '@tauri-apps/api/core';
import { getCurrentWindow } from '@tauri-apps/api/window';
async function initializeFrontend(): Promise<void> {
// Your initialization logic
const config = await loadConfig();
await setupRouter();
await preloadAssets();
// Signal completion
await invoke('set_complete', { task: 'frontend' });
}
// Wait for window to be fully ready
getCurrentWindow().once('tauri://created', () => {
initializeFrontend();
});
Backend Setup
Add Tokio Dependency
cargo add tokio --features time
Rust Implementation
In src-tauri/src/lib.rs:
use std::sync::Mutex;
use tauri::{AppHandle, Manager, State};
// Track initialization state
struct SetupState {
frontend_task: bool,
backend_task: bool,
}
impl Default for SetupState {
fn default() -> Self {
Self {
frontend_task: false,
backend_task: false,
}
}
}
// Command to mark tasks complete
#[tauri::command]
async fn set_complete(
app: AppHandle,
state: State<'_, Mutex<SetupState>>,
task: String,
) -> Result<(), String> {
let mut state = state.lock().map_err(|e| e.to_string())?;
match task.as_str() {
"frontend" => state.frontend_task = true,
"backend" => state.backend_task = true,
_ => return Err(format!("Unknown task: {}", task)),
}
// Check if all tasks are complete
if state.frontend_task && state.backend_task {
// Close splash and show main window
if let Some(splash) = app.get_webview_window("splashscreen") {
splash.close().map_err(|e| e.to_string())?;
}
if let Some(main) = app.get_webview_window("main") {
main.show().map_err(|e| e.to_string())?;
main.set_focus().map_err(|e| e.to_string())?;
}
}
Ok(())
}
// Backend initialization
async fn setup_backend(app: AppHandle) {
// IMPORTANT: Use tokio::time::sleep, NOT std::thread::sleep
// std::thread::sleep blocks the entire async runtime
// Perform backend initialization:
// - Database connections
// - Load configuration
// - Initialize services
// Example: simulate work
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
// Mark backend as complete
if let Some(state) = app.try_state::<Mutex<SetupState>>() {
let _ = set_complete(
app.clone(),
state,
"backend".to_string(),
).await;
}
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(Mutex::new(SetupState::default()))
.invoke_handler(tauri::generate_handler![set_complete])
.setup(|app| {
let handle = app.handle().clone();
// Spawn backend initialization
tauri::async_runtime::spawn(async move {
setup_backend(handle).await;
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Simple Implementation
For simpler cases where you only need to wait for the frontend:
Configuration
{
"app": {
"windows": [
{
"label": "main",
"visible": false
},
{
"label": "splashscreen",
"url": "splashscreen.html",
"width": 400,
"height": 300,
"decorations": false
}
]
}
}
Frontend
import { invoke } from '@tauri-apps/api/core';
async function init() {
// Initialize your app
await setupApp();
// Close splash, show main
await invoke('close_splashscreen');
}
document.addEventListener('DOMContentLoaded', init);
Backend
use tauri::{AppHandle, Manager};
#[tauri::command]
async fn close_splashscreen(app: AppHandle) -> Result<(), String> {
if let Some(splash) = app.get_webview_window("splashscreen") {
splash.close().map_err(|e| e.to_string())?;
}
if let Some(main) = app.get_webview_window("main") {
main.show().map_err(|e| e.to_string())?;
main.set_focus().map_err(|e| e.to_string())?;
}
Ok(())
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![close_splashscreen])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Styling Variations
Minimal Splash
<style>
.splash-container {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background: #ffffff;
}
.logo {
width: 120px;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(0.95); }
}
</style>
Progress Bar Splash
<style>
.progress-container {
width: 200px;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
overflow: hidden;
margin-top: 20px;
}
.progress-bar {
height: 100%;
background: #4f46e5;
animation: progress 2s ease-in-out infinite;
}
@keyframes progress {
0% { width: 0%; }
50% { width: 70%; }
100% { width: 100%; }
}
</style>
<div class="progress-container">
<div class="progress-bar"></div>
</div>
Dark Theme with Glow
<style>
.splash-container {
background: #0a0a0a;
color: #ffffff;
}
.logo {
filter: drop-shadow(0 0 20px rgba(79, 70, 229, 0.5));
}
.app-name {
text-shadow: 0 0 20px rgba(79, 70, 229, 0.5);
}
</style>
Important Notes
-
Async Sleep: Always use
tokio::time::sleepin async Rust code, neverstd::thread::sleep. The latter blocks the entire runtime. -
Window Labels: Ensure window labels in code match those in
tauri.conf.json. -
Error Handling: The splash screen should handle errors gracefully. If initialization fails, show the main window anyway with an error state.
-
Timing: Keep splash screen visible long enough for branding but not so long it frustrates users. Aim for 1-3 seconds minimum.
-
Transparent Windows: When using
transparent: true, ensure your HTML hasbackground: transparentonhtmlandbodyelements. -
Mobile Considerations: On mobile platforms, splash screens work differently. Consider using platform-native splash screens for iOS and Android.
Troubleshooting
Splash screen doesn't appear:
- Verify the URL path is correct in
tauri.conf.json - Check that the HTML file exists in the correct location
Main window shows too early:
- Ensure
visible: falseis set on the main window - Verify the
set_completecommand is being called correctly
Transparent background not working:
- Set
transparent: truein window config - Set
background: transparentin CSS for html and body - On some platforms, you may need
decorations: false
Window position issues:
- Use
center: truefor centered splash screens - Or specify explicit
xandycoordinates
More from beshkenadze/claude-code-tauri-skills
setting-up-tauri-projects
Helps users create and initialize new Tauri v2 projects for building cross-platform desktop and mobile applications. Covers system prerequisites and setup requirements for macOS, Windows, and Linux. Guides through project creation using create-tauri-app or manual Tauri CLI initialization. Explains project directory structure and configuration files. Supports vanilla JavaScript, TypeScript, React, Vue, Svelte, Angular, SolidJS, and Rust-based frontends.
3migrating-tauri-apps
Assists users with migrating Tauri applications from v1 to v2 stable, and from v2 beta to v2 stable, covering breaking changes, configuration updates, API migrations, and plugin system changes.
2configuring-tauri-http-headers
Guides developers through configuring HTTP headers security in Tauri v2 applications, covering security headers, custom headers, and CORS configuration for secure cross-origin resource handling.
2developing-tauri-plugins
Guides the user through Tauri plugin development, including creating plugin extensions, configuring permissions, and building mobile plugins for iOS and Android platforms.
2configuring-tauri-apps
Guides developers through Tauri v2 configuration including tauri.conf.json structure, Cargo.toml settings, environment-specific configs, and common configuration options for desktop and mobile applications.
2configuring-tauri-csp
Guides users through configuring Content Security Policy (CSP) in Tauri v2 applications to prevent XSS attacks and enhance security by restricting resource loading.
2