Okay, let’s dive into crafting a unique and engaging entry point for your players.
We’re going to build a Custom FiveM Loading Screen from the ground up.
Table of Contents
What is a Loading Screen Resource?
A custom loading screen is often the very first interaction a player has with your specific FiveM server.
It’s a fantastic opportunity to establish your server’s brand, convey important information, and create an immersive atmosphere right from the start.
Forget the generic FiveM visuals; we want players to feel your server’s identity the moment they start connecting.
Here at QBCore Store LLC, we believe in empowering server owners with the tools and knowledge to create truly unique experiences.
This comprehensive guide will walk you through every step, from the basic HTML structure to styling with CSS, adding interactivity with JavaScript, and finally integrating it seamlessly into your FiveM server using Lua.
We’ll even cover how to hide that default FiveM bridge animation for a cleaner transition.
Whether you’re a coding novice or have some web development experience, we’ll break it down clearly.
Let’s get started on making your server stand out.
Why Bother with a Custom FiveM Loading Screen?
You might be wondering if it’s worth the effort.
Absolutely!
Think of it as the lobby or entryway to your virtual world.
First Impressions: It sets the tone and professionalism of your server immediately.
Branding: Reinforce your server’s name, logo, and overall theme.
Information Display: Share crucial information like rules, Discord links, website URLs, or server status updates before players even spawn in.
Engagement: Use music, dynamic messages, or even videos to keep players engaged during the loading process, reducing perceived wait times.
Uniqueness: Differentiate your server from the countless others using default or generic screens.
A well-designed loading screen shows you care about the details and the player experience.
Prerequisites
Before we start coding, let’s make sure you have the necessary tools and basic understanding:
- Text Editor: You’ll need a program to write your code.
- Visual Studio Code (VS Code): Free, powerful, and highly recommended, with many helpful extensions.
- Sublime Text: Another popular, lightweight option.
- Notepad++: A solid free choice for Windows users.
- Avoid using basic Notepad or TextEdit, as they lack features helpful for coding (like syntax highlighting).
- Basic Web Development Knowledge (Helpful, Not Essential):
- HTML (HyperText Markup Language): Understands the basic structure of a web page (tags like
<div>,<img>,<p>). We’ll provide the code, but knowing the basics helps. - CSS (Cascading Style Sheets): Knows how to style HTML elements (colors, sizes, positions). Again, we’ll guide you, but familiarity is a plus.
- JavaScript (JS): Understands basic programming concepts for adding interactivity. We’ll keep the JS relatively simple initially.
- HTML (HyperText Markup Language): Understands the basic structure of a web page (tags like
- FiveM Server Access: You need access to your server’s files, specifically the
resourcesfolder, to install the loading screen. - Image Editing Software (Optional): Tools like Photoshop, GIMP (free), or even Canva can be useful for creating or editing logos and background images.
- Patience and Willingness to Learn: Debugging and tweaking are part of the process!
Don’t worry if you’re not an expert in web development.
We’ll explain each step clearly and provide copy-pasteable code snippets.
Understanding How FiveM Loading Screens Work (NUI)
FiveM utilizes a system called NUI (Native UI) to display web pages inside the game.
Essentially, your custom loading screen is just a standard webpage (built with HTML, CSS, and JavaScript) that FiveM’s NUI system renders while the game assets are loading in the background.
This means we can leverage standard web technologies to create visually rich and interactive experiences.
The core components are:
index.html: The main file defining the structure and content of your loading screen.style.css: The file that defines the visual appearance (layout, colors, fonts, etc.).script.js: The file that adds dynamic behavior (like changing text, animations, music playback).fxmanifest.lua(or__resource.lua): A special FiveM file that tells the server this is a resource, specifies it’s a loading screen, and lists all the necessary files.
Now, let’s start building.
Step 1: Creating the Basic HTML Structure (index.html)
First, create a new folder for your loading screen resource. Let’s call it my-loading-screen.
Inside this folder, create a file named index.html.
This file will hold the skeleton of our loading screen.
We need containers for different elements: the background, a logo, loading progress indication, and areas for text messages.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Server Name - Loading...</title>
<!-- Link to your CSS file -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Main container for the entire screen -->
<div class="loading-container">
<!-- Background Element (handled by CSS) -->
<div class="background"></div>
<!-- Content Wrapper -->
<div class="content">
<!-- Logo Area -->
<div class="logo-area">
<img src="images/logo.png" alt="Server Logo" id="server-logo">
<!-- You can replace img with text if you prefer -->
<!-- <h1>My Awesome Server</h1> -->
</div>
<!-- Message Area -->
<div class="message-area">
<p id="loading-message">Initializing connection...</p>
<p id="dynamic-message">Welcome! Loading server assets...</p>
</div>
<!-- Progress Bar Area -->
<div class="progress-bar-container">
<div class="progress-bar">
<div class="progress-bar-inner" id="progress-bar-inner"></div>
</div>
<p id="progress-text">0%</p>
</div>
<!-- Music Control (Optional) -->
<div class="music-control">
<button id="play-pause-button">Pause Music</button>
<input type="range" id="volume-slider" min="0" max="1" step="0.01" value="0.5">
</div>
</div> <!-- End Content Wrapper -->
</div> <!-- End Loading Container -->
<!-- Link to your JavaScript file (place at the end of body) -->
<script src="script.js"></script>
</body>
</html>Explanation:
<!DOCTYPE html>&<html>: Standard HTML5 boilerplate.<head>: Contains meta-information and links to external resources.charset="UTF-8": Ensures proper character display.viewport: Important for responsive design (adapting to different screen sizes), though less critical for fixed-resolution game loading screens.<title>: Sets the text that might appear in a browser tab (less relevant in FiveM NUI but good practice).<link rel="stylesheet" href="style.css">: Connects our HTML to our CSS file for styling.
<body>: Contains the visible content of the page.<div class="loading-container">: The main wrapper for everything. We’ll use this for overall layout.<div class="background">: An empty div we’ll style with CSS to hold our background image or video.<div class="content">: Wraps the actual content (logo, text, progress bar) to help with centering and positioning.<div class="logo-area">: A container for your server’s logo.<img src="images/logo.png" ...>: An image tag. Important: You’ll need to create animagesfolder insidemy-loading-screenand place yourlogo.pngfile there. Make sure the filename matches!
<div class="message-area">: Holds text messages.- We give paragraphs IDs (
loading-message,dynamic-message) so we can easily target them with JavaScript later.
- We give paragraphs IDs (
<div class="progress-bar-container">: Holds the progress bar elements..progress-bar: The outer container of the bar..progress-bar-inner: The inner part that will fill up. We give it an ID (progress-bar-inner) for JS control.<p id="progress-text">: Displays the percentage text, also with an ID.
<div class="music-control">: (Optional) Basic controls for background music. IDs allow JS interaction.<script src="script.js">: Links our HTML to our JavaScript file. Placing it at the end of the<body>ensures the HTML elements exist before the script tries to interact with them.
Save this file as index.html in your my-loading-screen folder. Create an images subfolder and add a placeholder logo.png for now.
Step 2: Styling the Loading Screen (CSS – style.css)
Now, let’s make it look good!
Create a file named style.css in the same my-loading-screen folder.
This file controls the visual presentation.
/* Basic Reset & Body Styling */
* {
margin: 0;
padding: 0;
box-sizing: border-box; /* Makes width/height include padding and border */
}
body, html {
height: 100%;
width: 100%;
overflow: hidden; /* Hide scrollbars */
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; /* Example font */
color: #ffffff; /* Default text color (white) */
}
/* Main Container */
.loading-container {
position: relative; /* Needed for absolute positioning of children */
width: 100%;
height: 100%;
display: flex; /* Use flexbox for centering content */
justify-content: center;
align-items: center;
text-align: center;
}
/* Background Styling */
.background {
position: absolute; /* Take up full screen behind content */
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('images/background.jpg'); /* CHANGE THIS to your image */
background-size: cover; /* Scale image to cover the container */
background-position: center center; /* Center the image */
background-repeat: no-repeat;
z-index: -1; /* Place it behind other content */
filter: brightness(0.6); /* Optional: Darken the background slightly */
}
/* --- OR Use a Solid Color Background --- */
/*
.background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: #1a1a1a;
z-index: -1;
}
*/
/* Content Wrapper */
.content {
z-index: 1; /* Ensure content is above the background */
padding: 20px;
background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent black background */
border-radius: 10px;
max-width: 600px; /* Limit content width */
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
}
/* Logo Area */
.logo-area {
margin-bottom: 30px;
}
#server-logo {
max-width: 200px; /* Adjust max logo width */
height: auto; /* Maintain aspect ratio */
display: block; /* Allows margin auto to center */
margin-left: auto;
margin-right: auto;
}
/* Message Area */
.message-area {
margin-bottom: 30px;
}
#loading-message {
font-size: 1.2em;
font-weight: bold;
margin-bottom: 10px;
color: #cccccc;
}
#dynamic-message {
font-size: 1em;
min-height: 40px; /* Prevent layout shifts when message changes */
}
/* Progress Bar Area */
.progress-bar-container {
width: 80%; /* Width relative to the content container */
margin: 0 auto; /* Center the container */
margin-bottom: 20px;
}
.progress-bar {
width: 100%;
background-color: #555555; /* Dark grey background */
border-radius: 5px;
overflow: hidden; /* Hide overflowing inner bar */
height: 25px; /* Bar height */
border: 1px solid #333;
}
.progress-bar-inner {
height: 100%;
width: 0%; /* Start at 0% width */
background-color: #4CAF50; /* Green progress color */
border-radius: 5px 0 0 5px; /* Keep left radius */
transition: width 0.5s ease-in-out; /* Smooth transition for width changes */
text-align: center;
line-height: 25px; /* Vertically center text if needed inside */
color: white;
}
#progress-text {
margin-top: 5px;
font-size: 0.9em;
}
/* Music Control (Optional) */
.music-control {
margin-top: 25px;
display: flex; /* Arrange button and slider side-by-side */
justify-content: center;
align-items: center;
gap: 15px; /* Space between elements */
}
#play-pause-button {
padding: 8px 15px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 0.9em;
transition: background-color 0.3s ease;
}
#play-pause-button:hover {
background-color: #45a049;
}
#volume-slider {
cursor: pointer;
width: 150px; /* Adjust slider width */
}
/* Add some basic responsiveness if needed, though less critical in NUI */
@media (max-width: 600px) {
.content {
max-width: 90%;
}
.progress-bar-container {
width: 90%;
}
}Explanation:
* { box-sizing: border-box; }: A common reset to make sizing elements more predictable.body, html: Sets the base height/width and hides potential scrollbars. Sets a default font and text color..loading-container: Usesdisplay: flexto easily center the.contentdiv both horizontally (justify-content) and vertically (align-items).position: relativeis crucial for positioning the absolute background..background:position: absolute: Takes the element out of the normal flow and positions it relative to the nearest positioned ancestor (.loading-container).top: 0; left: 0; width: 100%; height: 100%;: Makes it cover the entire container.background-image: url(...): Crucially, change'images/background.jpg'to the actual path of your background image. Make sure the image is in theimagesfolder.background-size: cover: Scales the image nicely.z-index: -1: Pushes it behind other elements.filter: brightness(0.6): An optional effect to darken the background, making text more readable. Adjust or remove as needed.- Alternative: A commented-out section shows how to use a simple solid background color instead of an image.
.content:z-index: 1: Ensures it sits on top of the background.background-color: rgba(0, 0, 0, 0.5): A semi-transparent black background for the content area itself, helping text stand out against complex backgrounds. Adjust the last value (alpha) from 0 (fully transparent) to 1 (fully opaque).border-radius,max-width,box-shadow: Add some visual polish.
.logo-area,#server-logo: Styles the logo container and the logo image itself (setting max width, centering)..message-area,#loading-message,#dynamic-message: Styles the text elements (font size, color, margins).min-heightprevents the layout from jumping when the dynamic message content changes length..progress-bar-container,.progress-bar,.progress-bar-inner: Styles the progress bar.- The outer container (
.progress-bar) sets the background color and shape. - The inner bar (
.progress-bar-inner) is what grows. It starts atwidth: 0%. We’ll change this width using JavaScript.transition: width 0.5s ease-in-out;makes the width change smooth.
- The outer container (
.music-control,#play-pause-button,#volume-slider: Styles the optional music controls using flexbox for layout and adding basic button styling.@media (max-width: 600px): A simple example of a media query for responsiveness. It adjusts the content width on smaller screens (less critical for FiveM but good practice).
Save this as style.css. Remember to create the images folder and add your background.jpg (or whatever you named it) and logo.png.
At this point, you could technically open the index.html file directly in your web browser (like Chrome or Firefox) to preview its static appearance!
Step 3: Adding Interactivity & Dynamic Content (JavaScript – script.js)
Now let’s breathe some life into our static page using JavaScript.
Create a file named script.js in your my-loading-screen folder.
We’ll add functionality for:
- Simulating loading progress.
- Displaying dynamic/changing messages.
- Adding background music with controls.
- Handling FiveM NUI events (the proper way to get loading progress).
// Wait for the DOM (Document Object Model - the HTML structure) to be fully loaded
document.addEventListener('DOMContentLoaded', () => {
// --- Get References to HTML Elements ---
const progressBarInner = document.getElementById('progress-bar-inner');
const progressText = document.getElementById('progress-text');
const dynamicMessage = document.getElementById('dynamic-message');
const loadingMessage = document.getElementById('loading-message'); // To update stages
// --- Configuration ---
const messages = [
"Loading core systems...",
"Establishing network connection...",
"Downloading latest server assets...",
"Syncing player data...",
"Parsing map details...",
"Almost there, preparing the world...",
"Tip: Visit our Discord at discord.gg/yourinvite",
"Tip: Check the rules on our website yourwebsite.com",
"Welcome to Our Awesome Server!"
];
let currentMessageIndex = 0;
const messageChangeInterval = 5000; // Change message every 5 seconds (5000ms)
// Background Music (Optional)
const backgroundMusic = new Audio('audio/background_music.ogg'); // IMPORTANT: Use .ogg for FiveM compatibility
backgroundMusic.volume = 0.5; // Set initial volume (0.0 to 1.0)
backgroundMusic.loop = true; // Loop the music
const playPauseButton = document.getElementById('play-pause-button');
const volumeSlider = document.getElementById('volume-slider');
let isPlaying = false; // Track music state
// --- Functions ---
// Function to update the progress bar and text
function updateProgress(percentage) {
percentage = Math.min(100, Math.max(0, percentage)); // Clamp between 0 and 100
progressBarInner.style.width = `${percentage}%`;
progressText.textContent = `${Math.round(percentage)}%`;
}
// Function to change the dynamic message
function changeDynamicMessage() {
dynamicMessage.style.opacity = 0; // Fade out
setTimeout(() => {
currentMessageIndex = (currentMessageIndex + 1) % messages.length;
dynamicMessage.textContent = messages[currentMessageIndex];
dynamicMessage.style.opacity = 1; // Fade in
}, 500); // Wait for fade out transition (0.5s)
}
// Function to attempt playing music (handles browser autoplay restrictions)
function playMusic() {
backgroundMusic.play().then(() => {
isPlaying = true;
playPauseButton.textContent = 'Pause Music';
console.log("Music started playing.");
}).catch(error => {
// Autoplay was prevented, common in browsers until user interaction
console.log("Music autoplay failed. Waiting for user interaction.", error);
isPlaying = false;
playPauseButton.textContent = 'Play Music';
// We might need a click listener on the body or button to initiate playback
});
}
// --- Initial Setup ---
// Set initial loading message
loadingMessage.textContent = "Initializing...";
updateProgress(0); // Start progress at 0%
// Start changing dynamic messages
dynamicMessage.textContent = messages[0]; // Show the first message immediately
setInterval(changeDynamicMessage, messageChangeInterval);
// Try to play music automatically
playMusic(); // Attempt background music playback
// --- Event Listeners ---
// Music Controls Event Listeners
playPauseButton.addEventListener('click', () => {
if (isPlaying) {
backgroundMusic.pause();
isPlaying = false;
playPauseButton.textContent = 'Play Music';
} else {
// Important: Re-trigger play function which handles potential initial failures
playMusic();
}
});
volumeSlider.addEventListener('input', (event) => {
backgroundMusic.volume = event.target.value;
});
// --- FiveM NUI Event Handling ---
// This is the CORE of interacting with the FiveM loading process
/*
FiveM NUI messages are sent via JavaScript events.
We listen for 'message' events on the window object.
The event 'data' property contains the information sent from Lua.
*/
window.addEventListener('message', function(event) {
const data = event.data;
// Check for the specific NUI message type used by FiveM for loading progress
// The 'loadstatus' event provides overall progress text.
if (data.type === 'loadstatus') {
if(data.status) {
loadingMessage.textContent = data.status;
}
}
// The 'progress' event provides detailed component progress (use this for the bar)
else if (data.eventName === 'progress') {
// data.loadFraction gives a value between 0.0 and 1.0
const progressPercentage = data.loadFraction * 100;
updateProgress(progressPercentage);
}
// A custom event we might send from Lua when loading is almost done
else if (data.type === 'loadingComplete') {
updateProgress(100);
loadingMessage.textContent = "Loading Complete! Joining server...";
// You could add fade-out effects here before the screen disappears
}
});
// --- Fallback/Simulated Progress (If NUI events aren't received or for testing) ---
// Comment this out or remove it if you rely solely on FiveM NUI events
/*
let simulatedProgress = 0;
const interval = setInterval(() => {
simulatedProgress += Math.random() * 5; // Increment by a random small amount
if (simulatedProgress >= 100) {
simulatedProgress = 100;
clearInterval(interval); // Stop the simulation when 100% is reached
loadingMessage.textContent = "Loading Complete! Joining server..."; // Update final message
}
updateProgress(simulatedProgress);
}, 300); // Update every 300ms
*/
// Add a small fade-in effect for the whole screen on load
document.body.style.opacity = 0;
setTimeout(() => {
document.body.style.transition = 'opacity 1s ease-in-out';
document.body.style.opacity = 1;
}, 100); // Start fade-in slightly after load
}); // End DOMContentLoadedExplanation:
document.addEventListener('DOMContentLoaded', () => { ... });: This ensures that the JavaScript code only runs after the entire HTML page structure has been loaded and is ready to be manipulated.- Element References: We get references to the HTML elements we need to interact with using
document.getElementById(). This is why having unique IDs in the HTML is important. - Configuration:
messages: An array holding the different text strings you want to cycle through in the dynamic message area. Customize these!currentMessageIndex: Keeps track of which message is currently displayed.messageChangeInterval: Sets how often (in milliseconds) the message changes.
- Background Music Setup:
new Audio('audio/background_music.ogg'): Creates an HTML audio object. Crucially:- Create an
audiofolder insidemy-loading-screen. - Place your background music file there.
- Use the
.oggformat! MP3 and other formats can be unreliable or not work at all within FiveM NUI. You can easily find online converters to change MP3 to OGG.
- Create an
backgroundMusic.volume: Sets the initial volume (0.0 = silent, 1.0 = full).backgroundMusic.loop = true;: Makes the music repeat.- We also get references to the play/pause button and volume slider.
updateProgress(percentage)function: Takes a number (0-100), clamps it to ensure it’s within bounds, updates thewidthstyle of the inner progress bar element, and changes the text content of the percentage display.changeDynamicMessage()function:- Uses
setIntervalin the setup phase to call this function repeatedly. - It calculates the index of the next message, wrapping around using the modulo operator (
%). - Updates the
textContentof thedynamicMessageelement. - Bonus: Includes a simple fade-out/fade-in effect using CSS opacity and
setTimeoutfor a smoother transition. Addtransition: opacity 0.5s ease-in-out;to the.message-area pselector in your CSS for this to work visually.
- Uses
playMusic()function: Attempts to play the music usingbackgroundMusic.play(). The.then()handles successful playback, while.catch()handles errors, which often occur due to browser autoplay restrictions (requiring user interaction first). It updates the button text accordingly.- Initial Setup: Sets the initial text, resets progress to 0, displays the first dynamic message, and starts the interval timer for message changes. It also calls
playMusic()to try and start the audio. - Event Listeners (Music Controls):
- Listens for clicks on the
playPauseButton. If music is playing, it pauses it; otherwise, it callsplayMusic()again (important to handle cases where initial autoplay failed). - Listens for
inputevents on thevolumeSlider(fires continuously as the slider moves) and updates thebackgroundMusic.volume.
- Listens for clicks on the
- FiveM NUI Event Handling (
window.addEventListener('message', ...)):- This is the most important part for real integration. FiveM sends messages to the NUI window (your HTML page) using the
postMessageAPI. - We listen for these messages on the
windowobject. event.datacontains the payload sent from FiveM’s Lua scripts.- We check
event.data.typeorevent.data.eventName(different FiveM versions/contexts might use slightly different structures) to see what kind of message it is. 'loadstatus': Often contains general status text (e.g., “Loading map,” “Initializing scripts”). We update theloadingMessageparagraph.'progress': This is typically used for the actual loading bar progress.data.loadFractionusually provides a value from 0.0 to 1.0, which we convert to a percentage and feed into ourupdateProgressfunction.'loadingComplete': This isn’t a standard FiveM event, but an example of a custom message you could send from a Lua script (which we’ll discuss later) to signal the end of loading, allowing you to set progress to 100% and show a final message.
- This is the most important part for real integration. FiveM sends messages to the NUI window (your HTML page) using the
- Fallback/Simulated Progress:
- The commented-out section provides a basic simulation of progress. It uses
setIntervalto increment the progress bar by a small random amount periodically. - This is useful for testing your loading screen visually in a browser without running FiveM.
- You should REMOVE or COMMENT OUT this simulation code when using the actual FiveM NUI events, otherwise, you might see conflicting progress updates.
- The commented-out section provides a basic simulation of progress. It uses
- Fade-in Effect: Adds a subtle fade-in for the entire body when the page loads for a smoother appearance.
Save this file as script.js. Remember to create the audio folder and add your .ogg music file.
Step 4: Integrating with FiveM (Lua – fxmanifest.lua)
Now we need to tell the FiveM server about our new resource and identify it as a loading screen.
Create a file named fxmanifest.lua in the root of your my-loading-screen folder.
-- Resource Manifest
fx_version 'cerulean' -- Use 'cerulean' or a newer version like 'adamant' or 'bodacious'
game 'gta5'
author 'Your Name or Server Name'
description 'Custom Loading Screen for My Awesome Server'
version '1.0.0'
-- Specify this resource as the loading screen
loadscreen 'index.html'
-- List all files needed by the UI (HTML, CSS, JS, images, audio, fonts, etc.)
files {
'index.html',
'style.css',
'script.js',
'images/logo.png',
'images/background.jpg', -- Add all your images here
'audio/background_music.ogg' -- Add all your audio files here
-- 'fonts/mycustomfont.woff2' -- Add custom fonts if you use any
}
-- Optional: Client script for advanced control (like hiding default elements)
client_script 'client.lua'
-- Optional: If your loading screen needs to fetch data FROM the server (more advanced)
-- server_script 'server.lua'
-- Optional: Define NUI settings if needed (rarely required for basic loading screens)
-- nui_settings {
-- ['scriptFramePolicy'] = "frame-ancestors 'self' https://cfx.re" -- Example security policy
-- }Explanation:
fx_version 'cerulean': Defines the manifest version. ‘cerulean’ is a common baseline, but newer versions like ‘adamant’ or ‘bodacious’ exist. Stick with ‘cerulean’ unless you need features from newer versions.game 'gta5': Specifies the game this resource is for.author,description,version: Metadata about your resource. Fill these in appropriately.loadscreen 'index.html': This is the crucial line. It tells FiveM to use the specified HTML file (index.htmlin our case) as the game’s loading screen.files { ... }: Very important! You must list every single file that your HTML page needs to load, relative to the resource’s root folder. This includes:- The HTML file itself (
index.html) - The CSS file (
style.css) - The JavaScript file (
script.js) - All images (e.g.,
images/logo.png,images/background.jpg) - All audio files (e.g.,
audio/background_music.ogg) - Any custom fonts you might have linked in your CSS.
- If you forget a file here, it won’t load in-game!
- The HTML file itself (
client_script 'client.lua': We include this because we’ll create a small client script in the next step to handle hiding the default FiveM loading elements.server_script 'server.lua': Only needed for advanced scenarios where your loading screen needs to communicate back to the server (e.g., fetching dynamic player counts before the main game environment loads, which is complex). We won’t use this for a basic setup.nui_settings: Allows setting specific security policies for the NUI frame. Generally not needed for standard loading screens unless you’re embedding external content or dealing with complex interactions.
Save this file as fxmanifest.lua.
(Note: Older servers might use __resource.lua instead of fxmanifest.lua. The syntax is very similar, but fx_version is usually omitted or different, and directives might vary slightly. fxmanifest.lua is the modern standard).
Step 5: Disabling the Default FiveM Bridge Animation (Lua – client.lua)
By default, FiveM shows its own loading text and sometimes a “bridge” loading animation before your custom screen fully takes over. We can hide these for a cleaner look using a client-side Lua script.
Create a file named client.lua in your my-loading-screen folder.
-- client.lua for the loading screen resource
-- This code runs as soon as the resource starts on the client
-- We wait a brief moment to ensure NUI is likely ready
Citizen.Wait(100)
-- Method 1: Using ShutdownLoadingScreenNui (Recommended for simple hiding)
-- This attempts to immediately hide the default FiveM loading GUI elements.
-- It's often effective, but timing can sometimes be tricky depending on client load speed.
ShutdownLoadingScreenNui()
-- You can also send a message to your NUI page if needed, for example,
-- to signal that Lua is ready or pass initial data.
-- SendNUIMessage({
-- type = "luaReady",
-- message = "Client script has loaded!"
-- })
-- Method 2: More controlled hiding using CreateThread and AddTextEntry
-- This method continuously overrides the default loading text entries.
-- It can be more reliable in ensuring default text doesn't flicker briefly.
-- Uncomment this section and comment out ShutdownLoadingScreenNui() if you prefer this.
--[[
Citizen.CreateThread(function()
-- Hide the default "Initializing..." text components
AddTextEntry('FE_THDR_GTAO', ' ') -- Loading Online
AddTextEntry('PM_NAME_APP', ' ') -- FiveM Application Name (might vary)
AddTextEntry('PM_INFO_DET', ' ') -- Build Info / Connecting status
AddTextEntry('LOADING_SPLAYER_L', ' ') -- Loading Story Mode (sometimes appears)
AddTextEntry('DLC_ITEM_UNLOCK', ' ') -- Unlock messages if any
-- Keep overriding them periodically while the custom screen is expected to be active
-- This loop might be excessive; often just setting them once is enough.
-- Adjust the Wait time or remove the loop if performance is impacted.
while true do
Citizen.Wait(500) -- Check/override every 500ms
-- Check if the loading screen is still active (pseudo-code, needs actual logic)
-- local isLoading = GetIsLoadingScreenActive() -- This native might not work early enough
-- if not isLoading then break end -- Exit loop when main game loads (needs better condition)
-- Re-apply overrides just in case
AddTextEntry('FE_THDR_GTAO', ' ')
AddTextEntry('PM_NAME_APP', ' ')
AddTextEntry('PM_INFO_DET', ' ')
AddTextEntry('LOADING_SPLAYER_L', ' ')
AddTextEntry('DLC_ITEM_UNLOCK', ' ')
-- Optionally, hide the rotating loading circle in the bottom right
HideHudComponentThisFrame(14) -- HUD_LOADING_SPINNER
end
end)
--]]
-- You can add more logic here if needed, for example, listening for game events
-- to send specific messages to your NUI loading screen.
-- Example: Send a message when the player spawns (though loading screen is usually gone by then)
-- AddEventHandler('playerSpawned', function()
-- SendNUIMessage({ type = 'playerReady' })
-- end)
print('[MyLoadingScreen] Client script loaded.')Explanation:
Citizen.Wait(100): A small delay. Sometimes trying to interact with NUI or game elements immediately when the script loads can fail. This gives things a moment to initialize.ShutdownLoadingScreenNui(): This is a built-in FiveM native function specifically designed to hide the default loading screen UI elements provided by the game/FiveM itself. It’s usually the simplest and most direct way.SendNUIMessage({ ... }): An example showing how you can send data from Lua to your JavaScript. The table you pass becomes theevent.dataobject in yourwindow.addEventListener('message', ...)listener inscript.js. You could use this to trigger specific actions or pass server information.- Method 2 (Commented Out):
- Provides an alternative approach using
AddTextEntry. This function allows you to override default game text strings identified by their keys (likeFE_THDR_GTAO). By setting them to a space (‘ ‘), you effectively hide them. - The
Citizen.CreateThreadcreates a separate thread for this task. - The
while trueloop (withCitizen.Wait) continuously reapplies these overrides. This can be more robust against the game trying to reset the text but might be overkill. It also includesHideHudComponentThisFrame(14)to hide the spinner. - Choose one method. Using
ShutdownLoadingScreenNui()is generally preferred for simplicity unless you encounter issues where default elements still flash briefly.
- Provides an alternative approach using
print(...): Logs a message to the client’s F8 console, useful for confirming the script loaded.
Save this file as client.lua.
Step 6: Installing and Running the Loading Screen
Now that all the pieces are created, let’s put it on the server.
- Upload the Resource:
- Take the entire
my-loading-screenfolder (which now containsindex.html,style.css,script.js,fxmanifest.lua,client.lua, and theimagesandaudiosubfolders with their content). - Upload this complete folder to your FiveM server’s
resourcesdirectory. You might use FTP software (like FileZilla) or your server host’s web panel. The structure should look like:[server-data]/resources/my-loading-screen/.
- Take the entire
- Ensure the Resource in
server.cfg:- Open your server’s main configuration file, usually named
server.cfg. - Find the section where resources are started (lines usually beginning with
ensureorstart). - Add a line to start your loading screen resource:
cfg # Custom Loading Screen ensure my-loading-screen - Placement matters slightly: Ensure it’s listed before resources that might take a long time to load if you want the screen to appear as early as possible. However, basic
ensureis usually sufficient. Do not place it inside any[category]brackets if you want it to be a default resource.
- Open your server’s main configuration file, usually named
- Restart Your Server: For the changes in
server.cfgand the new resource to be recognized, you must fully restart your FiveM server. - Connect and Test: Launch FiveM and connect to your server. You should now see your custom loading screen instead of the default one! Test the progress bar (it should react to the actual FiveM loading), the changing messages, and the music controls. Check the F8 console in-game for any errors from your
client.luaor potential NUI issues. Check the browser console (often accessible via F8 -> NUI Tools, or by opening the HTML directly) for JavaScript errors.
Advanced Customization Ideas
Once you have the basics working, you can explore more advanced features:
- Background Videos: Instead of a static image, use an HTML
<video>tag.- Add
<video autoplay muted loop id="bg-video"><source src="videos/myvideo.mp4" type="video/mp4"></video>to yourindex.html. - Style
#bg-videoin CSS similar to the.backgrounddiv (absolute position, 100% width/height,object-fit: cover,z-index: -1). - Important: Videos significantly increase loading screen size. Optimize them heavily (resolution, bitrate). Use formats like
.mp4(H.264 codec). Remember to add the video file tofxmanifest.lua. Autoplay might requiremutedattribute initially due to browser policies; you might need JS to unmute based on user interaction (like clicking the volume control).
- Add
- Fetching Server Rules/Messages Dynamically: Instead of hardcoding messages in JS, use
fetchin yourscript.jsto load rules or announcements from a.jsonfile within your resource or even from an external web server/API. This makes updates easier. - Using Web Frameworks: Employ CSS frameworks like Tailwind CSS or Bootstrap for faster styling, or JavaScript frameworks like Vue.js or React for more complex UI logic (though this adds significant complexity and build steps).
- API Integrations: Fetch data from external APIs (e.g., show online player count from your Discord server using a Discord bot and a simple API endpoint). This requires server-side scripting (
server.luain your resource or a separate web service) to handle securely. - More Sophisticated Animations: Use CSS animations (
@keyframes) or JavaScript animation libraries (like GSAP) for smoother transitions, fading effects, or animated logos.
Troubleshooting Common Issues
- Loading Screen Doesn’t Appear:
- Check
server.cfg: Isensure my-loading-screenpresent and spelled correctly? Are there any errors in the server console on startup related to the resource? - Check
fxmanifest.lua: Is theloadscreen 'index.html'line correct? Are all necessary files (HTML, CSS, JS, images, audio) listed in thefilesblock? Check filenames and paths carefully (case-sensitive on Linux!). - Check Folder Structure: Is the
my-loading-screenfolder directly inside theresourcesfolder?
- Check
- CSS Styles Not Applied:
- Check HTML
<link>tag: Is thehref="style.css"correct? - Check
fxmanifest.lua: Isstyle.csslisted in thefilesblock? - Check CSS Syntax: Are there typos or errors in your
style.cssfile? Use a CSS validator. - Browser Cache: Sometimes FiveM’s NUI cache holds onto old versions. Clear your FiveM cache (usually in
%localappdata%\FiveM\FiveM.app\cacheon Windows, delete folders likebrowser,db,nui-storage) and restart FiveM.
- Check HTML
- JavaScript Not Working (No progress, no messages changing, no music):
- Check HTML
<script>tag: Is thesrc="script.js"correct and placed at the end of the<body>? - Check
fxmanifest.lua: Isscript.jslisted in thefilesblock? - Check Browser Console: Open the F8 console in FiveM, go to NUI Devtools (if available) or open the
index.htmldirectly in a browser and check the developer console (usually F12) for JavaScript errors. These errors often pinpoint the exact problem line. - Audio Issues: Is the music file in
.oggformat? Is the path innew Audio(...)correct? Isaudio/your_music.ogglisted in the manifest? Remember browser autoplay restrictions – music might only start after clicking the play button.
- Check HTML
- Progress Bar Not Updating:
- Are you relying on the FiveM NUI events (
window.addEventListener('message', ...)? Ensure this code is active (not commented out). - Are the event names (
loadstatus,progress,loadFraction) correct? These can sometimes vary slightly between FiveM updates or specific game builds. Addconsole.log(JSON.stringify(event.data))inside the message listener to see exactly what data FiveM is sending. - Is the element ID (
progress-bar-inner) correct in both HTML and JS?
- Are you relying on the FiveM NUI events (
- Default FiveM Loading Elements Still Visible:
- Check
client.lua: Is the script running (check for theprintmessage in F8)? IsShutdownLoadingScreenNui()being called? If usingAddTextEntry, are the keys correct for your game build? Try increasing the initialCitizen.Wait().
- Check
Need a Premium Solution? Check Out QBCore Store LLC!
Building a loading screen from scratch is rewarding, but it can also be time-consuming, especially if you want advanced features and a highly polished design.
If you prefer a ready-to-go, professional solution, we’ve got you covered here at QBCore Store LLC.
We offer a curated selection of premium, feature-rich loading screens designed by experienced developers.
Benefits of QBCore Store LLC Paid Loading Screens:
- Professional Designs: Visually stunning and modern aesthetics.
- Advanced Features: Often include background videos, music players, multiple configurable sections, Discord integration previews, server status indicators, and more.
- Easy Configuration: Typically come with simple configuration files to customize text, logos, links, and features without needing deep code changes.
- Reliability & Support: Tested for compatibility and often come with developer support if you encounter issues.
- Save Time & Effort: Get a high-quality result instantly, letting you focus on other aspects of your server.
Explore our range of interfaces and loading screens to find the perfect fit for your server’s identity:
- QBCore Store LLC Loading Screens Category
- QBCore Store LLC Scripts Collection (Interfaces section often includes Loadingscreens)
Consider these popular options available on QBCore Store LLC:
- Modern Loading Screen V1: A sleek and clean option to get you started.
- Advanced Loading Screen V6: Packed with features for ultimate customization.
- Unique Loading Screen V13: Stand out with a distinctive design.
- Loading Screen V16: Another excellent choice with modern features.
Investing in a premium loading screen can significantly elevate your server’s perceived quality and player experience right from the first click.
Conclusion
Creating a Custom FiveM Loading Screen is a powerful way to enhance your server’s identity and provide a better user experience.
We’ve walked through setting up the HTML structure, styling it with CSS, adding dynamic behavior with JavaScript, and integrating it into FiveM using the fxmanifest.lua and a simple client.lua script to hide default elements.
Remember that the key is careful file management (listing everything in the manifest), understanding how NUI events work for real progress updates, and using web standards (HTML, CSS, JS).
Don’t be afraid to experiment with different styles, messages, and media.
Test thoroughly in your browser and in-game, using the developer consoles to debug issues.
Whether you build your own masterpiece following this guide or choose a polished premium option from QBCore Store LLC.com, investing in your loading screen is investing in your server’s first impression.
Happy coding, and we hope this helps you create an amazing entry point for your players!
Frequently Asked Questions (FAQ)
Q1: Can I use background videos instead of images?
A: Yes! Use the HTML <video> tag (<video autoplay muted loop id="bg-video"><source src="videos/your_video.mp4" type="video/mp4"></video>). Style it with CSS to cover the screen (position: absolute, width: 100%, height: 100%, object-fit: cover, z-index: -1). Remember to mute for autoplay to work reliably, optimize the video file size heavily, use compatible formats like MP4 (H.264), and list the video file in your fxmanifest.lua.
Q2: How do I make the progress bar show the actual FiveM loading progress?
A: The most reliable way is using the JavaScript window.addEventListener('message', ...) to listen for NUI messages sent by FiveM. Specifically, look for an event like progress which often contains a loadFraction property (a value from 0.0 to 1.0). Multiply this by 100 and pass it to your updateProgress JavaScript function. Avoid relying solely on simulated progress (like the setInterval example) for the final version.
Q3: Where exactly do I put the loading screen files on my server?
A: Create a dedicated folder for your resource (e.g., my-loading-screen) inside your server’s main resources directory. All files (index.html, style.css, script.js, fxmanifest.lua, client.lua, and subfolders like images, audio) should go inside this resource folder.
Q4: Can I have background music? How do I add controls?
A: Yes. Use the HTML <audio> tag or create an Audio object in JavaScript (new Audio('audio/music.ogg')). Crucially, use the .ogg audio format for best compatibility in FiveM NUI. Add standard HTML buttons (<button>) and potentially a range input (<input type="range">) for volume in your index.html. Use JavaScript event listeners (addEventListener) on these elements to control the audio object’s .play(), .pause(), and .volume properties. Remember to list the audio file in your manifest.
Q5: Why isn’t my loading screen showing up at all?
A: Double-check these common culprits:
1. Is the resource ensured correctly in server.cfg (ensure resource_folder_name)?
2. Is the fxmanifest.lua present in the resource folder?
3. Does the manifest have the loadscreen 'your_html_file.html' line?
4. Are all required files (HTML, CSS, JS, images, audio, fonts) listed accurately under files { ... } in the manifest? Check paths and filenames (case-sensitive!).
5. Are there any errors in the server console or client F8 console related to the resource failing to load?
6. Did you restart the server after adding the resource and ensuring it?
Q6: How can I make the loading screen fade out smoothly when the game starts?
A: This requires communication between your Lua script and the NUI page. FiveM doesn’t have a perfectly reliable “loading fully complete, about to spawn” event that’s easy to catch before the NUI is destroyed. However, you could:
1. Send a custom NUI message (SendNUIMessage({ type = 'loadingAlmostDone' })) from a client Lua script based on certain game events or timers just before spawn.
2. In your JavaScript, listen for this message (if (event.data.type === 'loadingAlmostDone')).
3. When received, trigger a CSS fade-out animation on your main container (.loading-container.fade-out { opacity: 0; transition: opacity 1s ease-out; } and add the fade-out class using JS). This gives a visual transition, though the NUI might still be abruptly removed by FiveM afterward.
Paid Loading Screens
0R-LOADINGSCREEN V2
Ursprünglicher Preis war: $25.00$18.00Aktueller Preis ist: $18.00.2NA Loadingscreen
Ursprünglicher Preis war: $33.00$14.00Aktueller Preis ist: $14.00.Custom Loading Screen
Ursprünglicher Preis war: $48.99$24.99Aktueller Preis ist: $24.99.Eyes Loading Screen
Ursprünglicher Preis war: $14.99$9.99Aktueller Preis ist: $9.99.izzy Loading Screen
Ursprünglicher Preis war: $37.00$30.00Aktueller Preis ist: $30.00.izzy Loading Screen v7
Ursprünglicher Preis war: $30.00$17.00Aktueller Preis ist: $17.00.Jakrino Loading Screen
Ursprünglicher Preis war: $14.00$9.00Aktueller Preis ist: $9.00.KS Loadingscreen
Ursprünglicher Preis war: $20.99$13.99Aktueller Preis ist: $13.99.Loading Screen (DebuX)
Ursprünglicher Preis war: $15.99$6.99Aktueller Preis ist: $6.99.
Free Loading Screens
Done! Any questions? Leave a comment.















