0Pricing
Lua Academy · Lesson

The require Function

Load external modules with require and understand module caching.

The require Function is a free Lua Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Lua Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

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

Frequently asked questions

Is the “The require Function” lesson free?

Yes — the full text of “The require Function” is free to read here on the web, and the Lua Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Lua Academy course, upgrade to CoddyKit PRO.

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; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

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