Quick answer: For compile-time known resources use preload(). For grouped collections, define a custom Resource holding an Array[PackedScene]. For runtime-discovered loads, use ResourceLoader.load_threaded_request.

Migrating a Godot 3 project to Godot 4 surfaces a sea of red errors: ResourcePreloader nodes can’t be instantiated. The class is gone. Three replacement patterns cover all the use cases the deprecated node served.

Pattern 1: preload for Compile-Time Constants

extends Node

const EnemyScene = preload("res://enemies/grunt.tscn")
const BulletScene = preload("res://projectiles/bullet.tscn")

func spawn_enemy():
    var e = EnemyScene.instantiate()
    add_child(e)

The preload runs at script parse, embedding the PackedScene in the script’s constant table. Instantiation is fast (no disk I/O). Use for any resource the script always needs.

Pattern 2: Resource Manifest

For grouped collections (a tile set, a level pack):

# level_manifest.gd
class_name LevelManifest extends Resource

@export var scenes: Array[PackedScene] = []
@export var names: Array[String] = []

Create an instance of LevelManifest via right-click in FileSystem → New Resource. Drag scenes into the array in the Inspector. Then:

const Manifest = preload("res://level_manifest.tres")

func load_level(idx: int):
    var packed = Manifest.scenes[idx]
    var scene = packed.instantiate()
    add_child(scene)

Manifest replaces ResourcePreloader’s “keyed bag of resources” with a typed Resource you can extend with metadata.

Pattern 3: Runtime Threaded Loading

For resources discovered at runtime (DLC, modded content):

func load_async(path: String):
    var err = ResourceLoader.load_threaded_request(path)
    if err != OK:
        push_error("load_threaded_request failed: %d" % err)
        return null

    while ResourceLoader.load_threaded_get_status(path) == ResourceLoader.THREAD_LOAD_IN_PROGRESS:
        await get_tree().process_frame

    return ResourceLoader.load_threaded_get(path)

The load happens on a background thread; your main thread polls without blocking. Show a loading UI in the meantime.

Migrating Existing ResourcePreloader Nodes

If your scene tree contains old ResourcePreloader instances:

  1. Open the .tscn in a text editor.
  2. Find [node ... type="ResourcePreloader"] blocks.
  3. List the resources stored in their resources property.
  4. Delete the ResourcePreloader nodes.
  5. Replace usage in scripts with preload() constants or a Manifest resource.

Save and reload in Godot 4 — the previously-broken scenes now open cleanly.

Verifying

Run the migrated scenes. Resources that previously came from the preloader should still spawn correctly. Use the Debugger’s Resources tab to confirm memory usage hasn’t exploded (preload + multiple references is fine; double-loading the same resource is a bug).

“Godot 4 has three loading patterns. ResourcePreloader was redundant; pick the one that matches your scenario.”

Manifest Resources are underrated — one .tres file replaces dozens of scattered preloads and gives you Inspector-driven authoring.