--- name: adding-tauri-splashscreen description: Guides the user through adding a Tauri splashscreen, splash screen, loading screen, or startup screen to their application. Covers configuration, custom splash HTML, closing the splash when ready, and styling. --- # 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: 1. Showing a splash window immediately on launch 2. Hiding the main window until ready 3. Performing initialization tasks (frontend and backend) 4. Closing splash and showing main window when complete ## Configuration ### Window Configuration Configure both windows in `tauri.conf.json`: ```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": false` on 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/`): ```html Loading
My Application
Loading...
``` ## Frontend Setup ### TypeScript/JavaScript Implementation In your main entry file (e.g., `src/main.ts`): ```typescript import { invoke } from '@tauri-apps/api/core'; // Helper function for delays function sleep(seconds: number): Promise { return new Promise(resolve => setTimeout(resolve, seconds * 1000)); } // Frontend initialization async function initializeFrontend(): Promise { // 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 ```typescript import { invoke } from '@tauri-apps/api/core'; import { getCurrentWindow } from '@tauri-apps/api/window'; async function initializeFrontend(): Promise { // 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 ```bash cargo add tokio --features time ``` ### Rust Implementation In `src-tauri/src/lib.rs`: ```rust 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>, 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::>() { 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 ```json { "app": { "windows": [ { "label": "main", "visible": false }, { "label": "splashscreen", "url": "splashscreen.html", "width": 400, "height": 300, "decorations": false } ] } } ``` ### Frontend ```typescript 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 ```rust 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 ```html ``` ### Progress Bar Splash ```html
``` ### Dark Theme with Glow ```html ``` ## Important Notes 1. **Async Sleep**: Always use `tokio::time::sleep` in async Rust code, never `std::thread::sleep`. The latter blocks the entire runtime. 2. **Window Labels**: Ensure window labels in code match those in `tauri.conf.json`. 3. **Error Handling**: The splash screen should handle errors gracefully. If initialization fails, show the main window anyway with an error state. 4. **Timing**: Keep splash screen visible long enough for branding but not so long it frustrates users. Aim for 1-3 seconds minimum. 5. **Transparent Windows**: When using `transparent: true`, ensure your HTML has `background: transparent` on `html` and `body` elements. 6. **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: false` is set on the main window - Verify the `set_complete` command is being called correctly **Transparent background not working:** - Set `transparent: true` in window config - Set `background: transparent` in CSS for html and body - On some platforms, you may need `decorations: false` **Window position issues:** - Use `center: true` for centered splash screens - Or specify explicit `x` and `y` coordinates