Quick Start
Let's build your first Nixi application! By the end of this guide, you'll have a working counter app with reactive updates.
Create a New Project
# Using Nixi CLI (after global installation)
nixi new myapp
# Or clone and run directly
git clone https://github.com/ijadux2/nixi.git
cd nixi
./bin/nixi new my-counter
# Or with nix run
nix run --command "nixi new my-counter"
This creates a new project directory with the following structure:
myapp/
├── src/
│ ├── nixi/ # Core framework modules
│ │ ├── init.lua # Main router
│ │ ├── style.lua # CSS DSL
│ │ ├── htmx.lua # HTMX bindings
│ │ ├── component.lua # HTML element creators
│ │ ├── ui/ # UI component library
│ │ │ ├── init.lua
│ │ │ ├── button.lua
│ │ │ ├── input.lua
│ │ │ ├── card.lua
│ │ │ └── theme.lua
│ │ └── ...
│ └── app.lua # Your application code
├── public/ # Static files
├── server.lua # Development server
└── package.json # Project metadata
Run the Server
cd my-counter
lua server.lua
Visit http://127.0.0.1:3000 to see your app.
Your First Route
Open src/app.lua and modify it:
#!/usr/bin/env lua
---@meta
package.path = "src/?.lua;src/?/init.lua;" .. package.path
local Nixi = require("nixi.init")
_G.Nixi = Nixi
-- Create application instance
local app = Nixi.new({
host = "127.0.0.1",
port = 3000,
})
-- Define the home route
app:get("/", function(ctx)
return Nixi.html([[
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My First Nixi App</title>
</head>
<body style="text-align: center; padding: 50px;">
<h1>Hello from Nixi!</h1>
<p>A Lua web framework with HTMX</p>
</body>
</html>
]])
end)
print("App loaded")
return app
Adding HTMX Interactivity
Let's add a counter that increments without page reloads:
local count = 0
-- Main page route
app:get("/", function(ctx)
return Nixi.Layout({
title = "Nixi Counter",
body = Nixi.div(
{ style = "text-align: center; padding: 50px;" },
Nixi.h1(nil, "Counter Demo"),
Nixi.p(nil, "Count: "),
Nixi.span({ id = "count" }, tostring(count)),
Nixi.div(
{ style = "margin-top: 20px; display: flex; gap: 10px;" },
Nixi.button(
{
["hx-get"] = "/decrement",
["hx-target"] = "#count",
["hx-swap"] = "innerHTML",
class = "btn"
},
"-"
),
Nixi.button(
{
["hx-get"] = "/increment",
["hx-target"] = "#count",
["hx-swap"] = "innerHTML",
class = "btn primary"
},
"+"
)
)
),
head = [[
<style>
.btn { padding: 12px 24px; font-size: 20px; border: none; border-radius: 8px; cursor: pointer; }
.primary { background: #00ff88; color: #1a1a2e; }
</style>
]]
})
end)
-- Increment endpoint
app:get("/increment", function(ctx)
count = count + 1
return Nixi.span({ id = "count" }, tostring(count))
end)
-- Decrement endpoint
app:get("/decrement", function(ctx)
count = count - 1
return Nixi.span({ id = "count" }, tostring(count))
end)
Understanding the Code
1. Setting Up Package Path
package.path = "src/?.lua;src/?/init.lua;" .. package.path
local Nixi = require("nixi.init")
_G.Nixi = Nixi
2. Creating an App
local app = Nixi.new({
host = "127.0.0.1",
port = 3000,
})
2. Defining Routes
app:get("/", function(ctx)
-- Handler code
end)
3. HTMX Attributes
| Attribute | Purpose |
|---|---|
hx-get="/endpoint" |
Make AJAX GET request to endpoint |
hx-target="#id" |
Target element to swap content into |
hx-swap="innerHTML" |
Replace inner HTML of target |
Building for Production
nixi build
# Output in ./dist directory
CSS DSL with For-Loops
Lua's for-loops enable generating hundreds of utility classes efficiently:
-- Generate spacing utilities (.m-1 to .m-16)
for i = 1, 16 do
Nixi.style.rule(".m-" .. i, {
margin = (i * 0.25) .. "rem"
})
Nixi.style.rule(".p-" .. i, {
padding = (i * 0.25) .. "rem"
})
end
-- Generate flexbox utilities
for _, align in ipairs({"start", "center", "end", "stretch"}) do
Nixi.style.rule(".items-" .. align, {
alignItems = align == "start" and "flex-start" or align == "end" and "flex-end" or align
})
end
-- Generate color utilities
local colors = {
{ name = "primary", value = "#7aa2f7" },
{ name = "danger", value = "#f7768e" },
{ name = "success", value = "#9ece6a" },
}
for _, color in ipairs(colors) do
Nixi.style.rule(".text-" .. color.name, { color = color.value })
Nixi.style.rule(".bg-" .. color.name, { backgroundColor = color.value })
end