Choosing require vs dofile in Lua: A Practical Decision Guide
Learn how to pick require or dofile for Lua modules, compare caching, error handling, and execution context, and see a concrete verification example.
30 Apr 2026, 01:35 UTC

Problem and Takeaway
You need to load a Lua module but must decide whether to use require or dofile. The useful takeaway is simple: use require for cached, reusable modules; use dofile only when you need fresh execution or explicit path control.
When to Use require
require loads a file once per Lua state, caches the returned table in package.loaded, and returns the same instance on subsequent calls. This is ideal for modules that contain definitions, functions, or objects that should persist across the program.
When to Use dofile
dofile executes the given file each time it is called, returning any values the script yields. It does not store the result in package.loaded, so the file runs again on every invocation. Use it when the script must run with a clean environment or when you need to pass a specific path.
Comparison Table
| Aspect | require | dofile |
|---|---|---|
| Caching | Yes – result stored in package.loaded |
No – each call re-executes the file |
| Path handling | Uses package.path and package.cpath for module name resolution |
Requires an explicit relative or absolute path |
| Error handling | Propagates Lua errors; can be wrapped with custom loaders | Same error propagation, but no loader customization |
| Side effects | Top-level code runs only once; later calls skip it | Top-level code runs on every call |
| Global pollution | Modules usually return a table, limiting globals | All globals defined in the file become globals in the caller |
Trade-offs Explained
The main trade-off is between efficiency and control. require saves time and ensures a single definition, but it can hide side effects if the module is expected to start fresh. dofile gives you full control over execution context, but you pay the cost of re-loading and risk polluting the global namespace.
Concrete Example
Assume a module mathutils.lua that increments a counter each load:
-- mathutils.lua
local counter = 0
function inc() counter = counter + 1 end
return { inc = inc }
Using require twice:
local mod1 = require 'mathutils'
local mod2 = require 'mathutils'
-- mod1 and mod2 are the same table (rawequal)
print(rawequal(mod1, mod2)) -- true
Using dofile twice:
local function run()
dofile 'mathutils.lua'
return counter
end
-- First call
print(run()) -- 1
-- Second call
print(run()) -- 2 (counter increased again)
Verification Steps
- Confirm require caching: In a Lua interpreter, run
require 'my_module'twice. Then executerawequal(mod1, mod2). The result should betrue, indicating the same table instance. - Observe dofile re-execution: Create a script
counter.luacontainingcounter = counter + 1and a return statement. Calldofile 'counter.lua'twice. Check that the printed counter values differ, proving the file runs each time. - Validate path resolution: Set
package.path = '/custom/dir/?.lua'in the interpreter, placecustom_module.luain that directory, then runrequire 'custom_module'. Verify the module loads without error, confirming thatrequirecorrectly resolves the path.
Each verification step must be performed in a Lua environment where you have read permission for the target file and write permission to modify package.path if needed. The main risk is unintended side effects: using require on a script with heavy initialization may cause state to persist unexpectedly, while dofile can introduce globals that clash with existing variables.
Conclusion
Choose require when you want a module loaded once, its return value cached, and path resolution handled automatically. Choose dofile when you need a fresh execution each time, an explicit file location, or you must avoid global state leakage. Verify your choice with the simple checks above to ensure the behavior matches your expectations.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.