Lua Academy · Lesson

The require Function

Load external modules with require and understand module caching.

Lesson 1 of 412 steps

The require Function is a free Lua Academy lesson on CoddyKit. This is lesson 1 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the Lua Academy learning path, and your progress syncs across the web and the CoddyKit app. The Lua Academy course includes 4 lessons in total.

How require Works

require(modname) loads and returns a module. It searches package.path for a .lua file and package.cpath for a C library. The result is cached in package.loaded[modname] — subsequent calls return the cached value without re-executing the file.

-- First require: loads and executes math_utils.lua
local math_utils = require("math_utils")

-- Second require: returns cached value (file not re-run)
local math_utils2 = require("math_utils")

print(math_utils == math_utils2)   -- true (same object)
print(package.loaded["math_utils"] == math_utils)  -- true

Module Return Value

A module file should return a value — typically a table of functions. This return value is what require gives back to the caller. If the file returns nothing (or true), that's what require returns.

-- mymodule.lua:
-- local M = {}
-- function M.hello() print("Hello from module!") end
-- return M

-- In main script:
local m = require("mymodule")
m.hello()   -- Hello from module!

-- The module table is the interface

Caching in package.loaded

package.loaded is a table mapping module names to their loaded values. You can manually set an entry to preload a mock, or set it to nil to force a reload (the module file will re-execute on next require).

-- Force reload by clearing cache
package.loaded["mymodule"] = nil
local fresh = require("mymodule")  -- re-executes the file

-- Preload a mock for testing
package.loaded["db"] = {
  query = function() return {} end,
  close = function() end,
}
local db = require("db")  -- gets the mock

package.path Format

package.path is a semicolon-separated list of patterns. The placeholder ? is replaced by the module name (with dots replaced by path separators). You can add directories to search by prepending to package.path.

print(package.path)
-- typically: ./?.lua;./?.luac;/usr/share/lua/5.4/?.lua;...

-- Add local lib directory
package.path = "./lib/?.lua;" .. package.path

-- Now require("utils") will look in ./lib/utils.lua

require vs dofile vs loadfile

require: caches, searches package.path. dofile(path): executes a file immediately, no caching, takes a direct path. loadfile(path): compiles but does not execute, returns a function. Use require for modules; dofile for one-off scripts.

-- dofile: no caching, direct path
dofile("./scripts/setup.lua")

-- loadfile: compile only, returns function
local fn, err = loadfile("./config.lua")
if fn then
  local result = fn()   -- execute when ready
end

-- require: best for modules
local json = require("json")

require with Dots for Directories

Use dots in module names to reference files in subdirectories. require("utils.string") maps to utils/string.lua (with ? in the path pattern). This is how multi-file libraries are organized.

-- Directory structure:
-- lib/
--   utils/
--     string.lua
--     table.lua
--   init.lua

package.path = "./lib/?.lua;" .. package.path

local strUtils = require("utils.string")   -- loads lib/utils/string.lua
local tblUtils = require("utils.table")    -- loads lib/utils/table.lua

Module Initialization Side Effects

Module files execute once when first required. Any code at the top level (outside functions) runs at require time. This is useful for one-time initialization, but be careful — side effects like network connections or file operations run at import time.

-- counter.lua:
-- local count = 0   -- initialized once on first require
-- local M = {}
-- function M.increment() count = count + 1 end
-- function M.get() return count end
-- return M

local c = require("counter")
c.increment()
c.increment()
print(c.get())   -- 2

local c2 = require("counter")
print(c2.get())  -- 2 (same instance, cached)

Error Handling in require

If a module file has a syntax error or throws during loading, require raises an error. The error message includes the module name and the error from inside the file. Use pcall to handle module load failures gracefully.

local ok, mod = pcall(require, "possibly_missing")
if not ok then
  print("Module not available:", mod)
  -- fall back to alternative or default implementation
  mod = {feature = function() return "fallback" end}
end

print(mod.feature())

package.preload

package.preload is a table where you can register loader functions by module name. When require("name") is called, Lua checks preload first. This lets you register modules without files — useful for embedded Lua or testing.

package.preload["mymath"] = function()
  return {
    double = function(n) return n * 2 end,
    triple = function(n) return n * 3 end,
  }
end

local m = require("mymath")
print(m.double(5))   -- 10
print(m.triple(5))   -- 15

Circular require

Circular dependencies (A requires B, B requires A) are partially handled: Lua stores a true placeholder in package.loaded before executing the module. If B tries to use A's unfinished table, it may get incomplete results. Avoid circular dependencies in module design.

-- a.lua: local b = require("b"); local M = {}; M.name = "A"; return M
-- b.lua: local a = require("a"); print("a.name:", a.name); return {}

-- When a.lua is loaded:
-- 1. a starts executing
-- 2. requires b -> b starts executing
-- 3. b requires a -> gets partial a (still loading!)
-- 4. a.name may be nil at step 3

print("Avoid circular requires in module design")

Quick Check

What happens when you call require("mod") a second time?

Recap: require

Summary:

  • require(name) loads once, caches in package.loaded
  • Module file returns its public interface (usually a table)
  • Clear package.loaded[name] to force reload
  • Add to package.path for custom search directories
  • Use package.preload for in-memory modules
  • Avoid circular dependencies
Free to start

Learn Lua with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
40
Lessons
159

Frequently Asked Questions

Is the “The require Function” lesson free?

Yes — any 3 lessons of the Lua Academy learning path, including “The require Function”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The Lua Academy course includes 4 lessons in total.

What will I learn in “The require Function”?

Load external modules with require and understand module caching. You practise Lua Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Lua Academy?

No prior experience is required. Lua Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 1 of 4.

How long does the “The require Function” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Lua Academy lesson?

Yes. Every Lua Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The require Function
  2. Writing a Module File
  3. package.path and package.cpath
  4. Module Patterns and Best Practices
← Back to Lua Academy