What a demo actually is
A demo is not a branch, and it is not a copy. It is a permanent, one-way version of your game with three properties that no ordinary branch has.
- It ships a subset. One level out of twelve, three weapons out of thirty. Everything else has to be genuinely absent, not merely unreferenced.
- It overrides a handful of files. A demo GameMode, a modified
DefaultEngine.ini, an end-of-demo screen with a wishlist button. - It never merges back. Nothing you do to make the demo a demo belongs in the full game.
But it still has to improve. When you fix a crash in the player controller on Monday, the demo should have that fix, because the demo is running the same player controller. That combination, changes flowing down and never flowing back, is what a copy cannot give you and what a branch is the wrong shape for.
Step 1: decide what the demo contains
Before touching anything, write down two things: the maps the player can reach, and where the demo stops. Everything else falls out of that.
This matters because of how Unreal actually works. You do not ship folders, you ship an asset graph. A level pulls in its meshes, which pull in their materials, which pull in their textures. Trying to describe a demo as a list of folders fails the first time an artist puts a Chapter 7 material in Content/Shared/Materials/, because now your folder rule either drags in a spoiler or breaks a level.
So the useful question is not "which folders?" It is "which maps?" A typical answer:
/Game/Maps/L_MainMenu
/Game/Maps/L_Tutorial
/Game/Maps/L_Chapter01
/Game/Blueprints/BP_DemoGameMode # the demo's own entry pointStep 2: create the demo
Open your project in the dashboard, go to Demos, and create one. Name it after what it is for: Steam Demo, Next Fest 2026, Press Build. You can have as many as you need on every plan, including Free.
A new demo starts empty. That is deliberate. The failure mode of an empty demo is "something is missing," which you notice in minutes. The failure mode of a demo that starts full is shipping content you never meant to show, which you notice when somebody posts it.
Step 3: let the editor work out what ships
Open your project in Unreal with the USourceControl plugin enabled and the desktop app running. In the editor console, ask what a demo built from your maps would actually contain:
USC.Demo.PreviewScope /Game/Maps/L_MainMenu /Game/Maps/L_Tutorial /Game/Maps/L_Chapter01This walks Unreal's own asset registry, the same dependency graph the cooker uses, and reports what it found. It writes nothing. Read the output before you commit to it:
18402 assets from 3 root(s); 0 broken reference(s), 12 soft reference(s) outside the demo
Soft reference leaves the demo: /Game/Blueprints/BP_Shop -> /Game/Items/DA_Chapter04Sword
Soft reference leaves the demo: /Game/UI/W_Codex -> /Game/Lore/DT_Chapter07EntriesThose two lines are the most valuable output in this entire guide. Soft references are lookups by name: FSoftObjectPath, a DataTable row, a LoadObjectcall with a string. They cook perfectly and then fail at runtime, which is why "the demo worked fine for me" and "the demo crashes when I open the shop" are both true. Either bring the target in, or make the code handle its absence.
When the report looks right, apply it:
USC.Demo.PushScope <demo-id> /Game/Maps/L_MainMenu /Game/Maps/L_Tutorial /Game/Maps/L_Chapter01The demo id is on its page in the dashboard. This sends the computed content set to your project, and the demo becomes exactly those assets.
No plugin, or not using the editor? You can define scope with include and exclude path rules on the demo's Contents tab instead. Rules are read top to bottom and the last match wins, so include Content/ followed by exclude Content/Maps/Chapter02 reads the way you would expect. It is less precise than the asset graph, so lean on it for whole trees rather than individual assets.
Step 4: open the demo in its own folder
In the desktop app, open the switcher in the project header. Your demos are listed under the main game. Pick one, and the app asks for a folder.
Give it a separate, empty one. This is the single most important practical decision in the whole workflow, and it is worth understanding why rather than just doing it.
A demo and the main game are both long lived. If they share one working folder, every switch between them rewrites Content/, which re-downloads gigabytes and invalidates your derived data cache and your shader cache. On a real project that is half an hour of staring at a progress bar before you can press Play. With separate folders, switching is instant and both trees keep their caches.
Step 5: change what needs to be different
Open the demo's folder in Unreal and work normally. Nearly every demo changes the same short list:
- A demo GameMode or GameInstance
DefaultEngine.iniandDefaultGame.ini, for the map list and the startup map- An end-of-demo screen, usually with a wishlist button
- A trigger that ends the demo: a time limit, a level cap, a door that does not open
- Splash, logo, and the build's version identifier
Commit those changes to the demo the way you commit anything else. There is nothing to configure first.
Here is the rule that matters, and it is the one thing to internalise from this guide: committing a file in the demo makes that file the demo's. From that moment the main game's changes to it stop arriving, because you clearly meant this version to be different.
That is exactly what you want for DefaultEngine.ini. It is usually not what you want for a shared character blueprint you touched to test something. The desktop app tells you before you commit, so it is a decision rather than an accident:
3 files will become demo-owned and stop getting the main game's updates.
Config/DefaultEngine.ini
Content/Blueprints/BP_DemoGameMode.uasset
Content/Blueprints/BP_PlayerCharacter.uasset <- probably not this oneIf you fork something by mistake, open the demo's What's different tab and use Return to the main game's. The file starts tracking again and catches up on the next update.
Step 6: let the main game keep improving the demo
This is the part that pays for the whole approach, and it needs no work from you. While a demo is taking updates, every commit to the main game flows into it automatically, for every file the demo has not made its own.
Fix a crash in the player controller on Monday, and the demo has that fix on Monday. Retune the movement, improve a material, optimise a mesh: the demo gets all of it, because it is running the same assets.
Files the demo owns are never touched automatically. If the main game also changes one of those, it goes into a review queue on the demo's Updatestab, showing both versions side by side. You choose per file, or leave it and keep the demo's version, which is what happens by default.
Step 7: freeze it before you ship
Two weeks or so before Next Fest, set the demo to Frozen on its page.
Frozen means updates from the main game stop applying. They keep arriving and stay visible, counted, waiting. The moment you unfreeze, they land. Hotfixes still go in normally, so you can fix a demo bug at 2am without unfreezing everything else.
This is not bureaucracy. Without it, the build you put in front of a hundred thousand new players includes whatever half finished refactor landed on Tuesday afternoon. Freezing is what makes the demo a thing you ship rather than a thing that happens to you.
Before you build, read the demo's Contents tab once and sign off. It lists what the demo ships and what it does not, and it records who checked and when. Ten minutes of somebody senior reading that list is cheap insurance on the riskiest build your studio ships.
Step 8: build and publish it as its own app
On Steam, a demo is a separate appwith its own App ID and its own depot. It is not a branch of your game's app. Request it in Steamworks first if you have not already.
Configure the demo's own store settings, separate from the main game's. Then package and upload it, either from the desktop app using your own Unreal install, or from a Forge runner so it happens on a build machine without anybody sitting there. Both work, and both use the demo's content and the demo's store settings.
Demos version independently. Your game can be on v1.4.0 while the demo is on v1.0.2, and both can ship a v1.0.2 without colliding.
Before any bytes move, a few things are checked:
✓ Publishing target Built from Steam Demo, publishing with Steam Demo's settings
✓ App ID is not the main game's 1234570 ≠ 1234560
✓ App ID is not another demo's not used by "Press Build"
✓ Demo scope verified 18402 assets, no excluded content on disk
✓ Download size 6.2 GB / 8 GB budgetThe second line is there because uploading a demo build to your game's App ID publishes your entire unreleased game to everyone who owns it. It takes one wrong number in one field, and it is not recoverable by deleting the build.
If you want the full store side of this, including depots, SteamPipe, and Steam Guard on an unattended machine, our guide to publishing an Unreal Engine game to Steam covers it end to end.
After the demo is live
Leave it frozen while it is live. When you want the demo to pick up the main game again, set it back to taking updates and every change that piled up applies at once, minus anything the demo owns.
If you fix something in the demo that belongs in the full game, cherry-pick that one commit up. That is deliberately a per fix action rather than a merge button: everything else about the demo exists precisely because it should not be in the main game.
Next year, when you build the next demo, archive this one instead of deleting it. Its content set and its history stay readable, so "what was in the Next Fest build?" has an answer.
Frequently asked
Can I just copy my project folder to make a demo?
You can, and it works until the main game moves. From then on every fix has to be made twice or hand-ported, and the demo drifts toward being a different game. A copy also doubles your storage and gives you no way to answer 'what is actually different in the demo?' A demo that shares its unchanged assets with the main game avoids all three problems.
Should I use a branch for my demo instead?
A branch is the wrong shape. Branches are meant to merge back, and nothing that makes a demo a demo belongs in the full game. Branches also have no concept of shipping a subset of your content, so the excluded chapters stay on disk and in the package. A demo flows one way, ships a defined subset, and never merges back.
How do I stop unreleased content from ending up in the demo build?
By making sure it is not there. Define the demo's contents from its maps, and the demo's folder holds only those assets plus what they reference. The build machine never has the rest, so no cook setting or packaging mistake can include it. That is stronger than filtering during cook, which is what a branch-based or copy-based demo leaves you relying on.
Will my demo get bug fixes from the main game automatically?
Yes, for every file the demo has not changed itself. Commit a fix to the main game and it flows into the demo without anyone doing anything. Files the demo has made its own are never overwritten automatically; if the main game also changes one, it goes into a review queue where you pick per file.
How many demos can I have?
As many as you need, on every plan including Free. Studios routinely run more than one: a Steam demo, a press build, and a showcase build can all be live at once with different content and different store settings. A demo shares every unchanged asset with the main game, so extra demos cost essentially no storage.
Does a Steam demo need its own App ID?
Yes. On Steam a demo is a separate app with its own App ID and depot, requested through Steamworks. Each demo carries its own store settings, and a publish is refused if the App ID matches the main game's or another demo's, because uploading a demo build to the full game's app would publish your unreleased game to everyone who owns it.
What are soft references and why do they break demos?
A soft reference is a lookup by name rather than a hard dependency: FSoftObjectPath, a DataTable row, a LoadObject call with a string. The cooker does not follow them, so a demo missing the target cooks cleanly and then fails at runtime when something tries to load it. This is the usual cause of a demo that works in the editor and crashes on level two. Computing your demo's contents from the asset registry reports every soft reference that leaves the demo, before you build.