Lua Code Formatter

Paste a Lua file and StyLua, compiled to WebAssembly, rewrites it with consistent indentation, spacing and quotes. Parse errors include a line:column range and what the parser expected.

Input

Settings

History

Load from URL

Lua code and StyLua

Lua turns up wherever a program needs a small embedded scripting language: Neovim and WezTerm configuration, LÖVE and Defold games, Roblox, World of Warcraft addons, Redis EVAL scripts, Nginx through OpenResty, and HAProxy. Its syntax is loose enough that hand-written files drift quickly, with statements packed onto one line, mixed quote styles and inconsistent indentation.

StyLua is an opinionated Lua formatter written in Rust and modelled on Prettier: it parses the file and prints it again from the syntax tree, so the result is the same however messy the input was. It is especially common among Neovim plugin authors, and many plugin repositories check formatting with it in CI. Here it runs as WebAssembly inside the page; your scripts are never sent to a server.

What StyLua changes

  • One statement per line. local a=1 local b=2 becomes two lines, and if not ok then return end is expanded into a three-line block.
  • Spacing. Spaces go around operators and after commas, and table constructors get padding: {a=1} becomes { a = 1 }.
  • Quotes. Strings prefer double quotes, so 'hello' becomes "hello". A string that would need escapes keeps the quote that avoids them, so 'say "hi"' is left alone. Long bracket strings like [[ ... ]] are never touched.
  • Call parentheses. Lua lets you call a function with a single string or table argument and no parentheses. StyLua adds them, so require 'socket' becomes require("socket") and setup{...} becomes setup({...}).
  • Line wrapping. Long calls and tables are broken over several lines at 80 columns. Anonymous functions passed as arguments, such as the callback in vim.keymap.set, keep the function() on the call line and close with end, {...}).

Comments are kept in place and require calls stay in the order you wrote them. For a Redis EVAL script, paste the Lua body itself rather than the whole redis-cli command, whose shell quoting is not Lua. To protect a hand-aligned table, put -- stylua: ignore on the line before a statement, or wrap a region in -- stylua: ignore start and -- stylua: ignore end.

Indentation and language versions

There are no Lua-specific options. The toolbar’s Indent decides between 2 spaces, 4 spaces and tabs. StyLua’s own default is tabs, while most Neovim configs use 2 spaces, so match whatever the rest of your repository uses. Lines are wrapped at 80 columns rather than StyLua’s usual 120, and StyLua treats that width as a target rather than a hard cap, so an unbreakable long string can still run past it. StyLua runs on Ctrl/Cmd+Enter, and Ctrl/Cmd+Shift+C copies.

The parser accepts Lua 5.1 through 5.4, which covers LuaJIT as well: goto and ::labels::, integer division //, bitwise operators and <const> and <close> attributes all work. Luau, the Roblox dialect, is only partly supported. A .luau file formats if it sticks to standard Lua, but type annotations such as a: number, type declarations, continue and compound assignments like x += 1 fail with a parse error.

Examples

Neovim keymaps and options

Each option assignment gets its own line, single quotes become double, and the callbacks stay attached to their calls.

Input
vim.keymap.set('n','<leader>ff',function() require('telescope.builtin').find_files({hidden=true}) end,{desc='Find files'})
vim.opt.number=true vim.opt.relativenumber=true vim.opt.tabstop=4
vim.api.nvim_create_autocmd('BufWritePre',{pattern='*.lua',callback=function() vim.lsp.buf.format() end})
Output
vim.keymap.set("n", "<leader>ff", function()
  require("telescope.builtin").find_files({ hidden = true })
end, { desc = "Find files" })
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.tabstop = 4
vim.api.nvim_create_autocmd("BufWritePre", {
  pattern = "*.lua",
  callback = function()
    vim.lsp.buf.format()
  end,
})
Open this example in the tool

Inventory class with metatables

Nested for and if blocks are indented with tabs, StyLua’s own default, and the table of items is spread over several lines.

Input
local Inventory={} Inventory.__index=Inventory
function Inventory.new(items) local self=setmetatable({},Inventory) self.items=items or {} return self end
function Inventory:total() local t=0 for _,item in ipairs(self.items) do if item.qty>0 then t=t+item.qty*item.price end end return t end
local inv=Inventory.new({{sku="KB-104",qty=1,price=129.9},{sku="MS-220",qty=2,price=39.5}})
print(string.format("total: %.2f",inv:total()))
Output
local Inventory = {}
Inventory.__index = Inventory
function Inventory.new(items)
	local self = setmetatable({}, Inventory)
	self.items = items or {}
	return self
end
function Inventory:total()
	local t = 0
	for _, item in ipairs(self.items) do
		if item.qty > 0 then
			t = t + item.qty * item.price
		end
	end
	return t
end
local inv = Inventory.new({
	{ sku = "KB-104", qty = 1, price = 129.9 },
	{ sku = "MS-220", qty = 2, price = 39.5 },
})
print(string.format("total: %.2f", inv:total()))
Open this example in the tool

LÖVE game loop with call shorthand

The parenthesis-free setTitle call gains parentheses, and the … concatenation gets spaces on both sides.

Input
local player={x=100,y=200,speed=180}
function love.load() love.window.setTitle 'Orbit' font=love.graphics.newFont(14) end
function love.update(dt) if love.keyboard.isDown('right') then player.x=player.x+player.speed*dt end end
function love.draw() love.graphics.print('Score: '..score,10,10) love.graphics.circle('fill',player.x,player.y,12) end
Output
local player = { x = 100, y = 200, speed = 180 }
function love.load()
    love.window.setTitle("Orbit")
    font = love.graphics.newFont(14)
end
function love.update(dt)
    if love.keyboard.isDown("right") then
        player.x = player.x + player.speed * dt
    end
end
function love.draw()
    love.graphics.print("Score: " .. score, 10, 10)
    love.graphics.circle("fill", player.x, player.y, 12)
end
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
error parsing: unexpected token `` (1:14 to 1:22), expected `end` to close function body blockA function has no closing end. The empty token in the message is the end of the input.Add end after the last statement of the function. Counting function, if, for and while openers against end keywords helps find which one is missing.
error parsing: unexpected token `` (1:19 to 1:19), expected `end` to conclude `if`An if … then block was never closed with end.Add end after the last statement of the if block.
error parsing: unclosed string (1:5 to 1:9)A quoted string is missing its closing quote. Regular Lua strings cannot span lines.Close the quote, or use a long bracket string [[ … ]] for text with line breaks.
error parsing: unexpected token `+` (1:13 to 1:14), expected expression after binary operatorAn expression ends with an operator and nothing after it, often from a line that was cut off.Finish the expression or remove the trailing operator.
error parsing:Some mistakes produce the bare prefix with only a position, for example a doubled comma in a table, = =, or Luau syntax such as x += 1 and type annotations.Look at the highlighted column. If the code is Luau, the error is expected: only standard Lua syntax is supported.

Frequently asked questions

Is the output identical to running stylua locally?

It matches StyLua with default settings except for two values taken from the page: the toolbar indentation and an 80-column width instead of StyLua’s 120. Set your stylua.toml to the same values for identical results.

Can I format Roblox Luau code?

Only the parts that are plain Lua. Type annotations, type aliases, continue and compound assignment operators are Luau extensions and fail to parse here.

Why were parentheses added to my require calls?

StyLua always writes call parentheses by default, turning require ‘x’ into require(“x”). Both forms do the same thing in Lua.

How do I keep a table aligned by hand?

Put – stylua: ignore on the line above the statement, or surround a section with – stylua: ignore start and – stylua: ignore end.

Does it work with LuaJIT and OpenResty code?

Yes. LuaJIT uses Lua 5.1 syntax with a few 5.2 additions such as goto, all of which the parser accepts.

Related tools