Appearance
Tutorial: Personal Todo List
Build a personal todo list application where logged-in users can create, complete, and delete their own todos. This tutorial covers user-scoped data, authentication gates, and multiple action pipelines.
What You Will Build
- A login-required todo list.
- An "add todo" form.
- A list of the user's todos with "mark complete" and "delete" buttons.
- User-scoped data -- each user sees only their own todos.
Step 1: Create the App and Table
- Go to WP-Nexus > Apps and click Create App.
- Name it
Todo App. - Go to the Tables tab and click Add Table.
- Name the table
todos. - Add these columns:
| Column Name | Type | Description |
|---|---|---|
title | varchar(255) | The todo text |
completed | tinyint | 0 = not done, 1 = done |
user_id | int | The WordPress user ID who owns this todo |
created_at | datetime | When it was created |
- Click Save Table.
TIP
Add a database index on the user_id column for better query performance. Open the table settings and add an index on user_id.
Step 2: Create the Component
- Go to the Components tab and click Add Component.
- Name it
Todo List.
Step 3: Set Login-Required Visibility
Select the root container of your component. In the Styles panel, set Visibility to logged_in.
Add a Text widget outside or above this container (or use a sibling) with visibility set to logged_out:
Text: "Please log in to manage your todos."
Visibility: logged_outStep 4: Build the Add Todo Form
Inside the logged-in container, build this widget tree:
Container (visibility: "logged_in")
├── Heading ("My Todos")
├── Form Container ← Add form
│ ├── Container (flex, row, gap: 8px)
│ │ ├── Text Input (name: "title", placeholder: "What needs to be done?", flex: 1)
│ │ └── Submit Button ("Add")
├── Divider
└── Repeater (todos data source) ← Todo list
└── (configured in Step 6)Step 5: Configure the Add Todo Pipeline
Select the Form Container and configure its onSubmit pipeline:
Pipeline
Context
→ Require Auth (logged_in)
→ Current User
→ Set Field (user_id = input.id)
→ Set Field (completed = 0)
→ Set Field (created_at)
→ Validate
→ Sanitize
→ Save Row (tableId: todos)
→ OutputNode Configurations
Require Auth:
require: "logged_in"Set Field (user_id):
To get the current user's ID, insert a Current User source node before the Set Field node. The Current User node outputs the user's profile data (including id). Then configure Set Field:
fieldName: "user_id"
expression: "input.id"INFO
The user_meta() expression function calls WordPress's get_user_meta() and is intended for custom meta fields, not core user properties like ID. Use the Current User source node to access core profile fields (id, email, display_name, etc.).
Set Field (completed):
fieldName: "completed"
expression: "0"Set Field (created_at):
fieldName: "created_at"
expression: "now()"Validate:
json
[
{ "field": "title", "rule": "required" },
{ "field": "title", "rule": "min_length", "value": "1" },
{ "field": "title", "rule": "max_length", "value": "255" }
]Sanitize:
json
[
{ "field": "title", "method": "strip_tags" },
{ "field": "title", "method": "trim" },
{ "field": "user_id", "method": "to_int" },
{ "field": "completed", "method": "to_int" }
]Save Row:
tableId: (your todos table ID)Step 6: Display Todos with a Repeater
Configure the Repeater data source:
Data Source Pipeline
Current User
→ Table Query (tableId: todos,
whereField: "user_id",
whereOp: "=",
whereValue: "`{ {input.id}}`",
orderBy: "created_at",
order: "DESC")
→ ResultThis fetches only the current user's todos.
Repeater Template
Inside the Repeater, add:
Container (flex, row, alignItems: center, gap: 12px, padding: 12px)
├── Checkbox (bound to row.completed, disabled) ← Visual indicator
├── Text ("`{ {row.title}}`", flex: 1)
├── Button ("Complete", variant: success) ← Mark complete
└── Button ("Delete", variant: danger) ← DeleteStyle completed items
Add a custom CSS class state on the todo Container:
className: "todo-completed"
expression: "row.completed == 1"Style the .todo-completed class with:
opacity: 0.5
textDecoration: line-through (on the text child)Show/hide the Complete button
Set the Complete button visibility to:
row.completed == 0So it only shows for incomplete todos.
Step 7: Configure the Mark Complete Action
Select the Complete button and configure its onClick action:
Pipeline
Context
→ Require Auth
→ Set Field (completed = 1)
→ Update Row (tableId: todos)
→ OutputSet Field:
fieldName: "completed"
expression: "1"The Context node provides the current row data (including id), which Update Row uses to identify the record.
Step 8: Configure the Delete Action
Select the Delete button and configure its onClick action:
Pipeline
Context
→ Require Auth
→ Delete Row (tableId: todos)
→ OutputThe Context provides the row's id, which Delete Row uses to remove the record.
TIP
Consider adding a confirm dialog on the Delete button to prevent accidental deletions. In the button settings, enable "Confirm before action" with a message like "Are you sure you want to delete this todo?"
Step 9: Add the Shortcode and Test
- Save the component.
- Create a shortcode in the Shortcodes tab.
- Add the shortcode to a WordPress page.
- Visit the page while logged in.
- Add a few todos.
- Mark one as complete and verify it shows as completed.
- Delete a todo and verify it disappears.
- Log out and verify the "Please log in" message appears.
- Log in as a different user and verify they see only their own todos.
What You Learned
- Scoping data to the current user with
whereField: "user_id". - Using Require Auth to protect write operations.
- Building multiple action pipelines (add, complete, delete) on the same page.
- Using custom CSS class states for data-driven styling.
- Conditional button visibility with expressions.
- Connecting Current User output to downstream node configs.
Next Steps
- Add a "due date" column and Date Input field.
- Add priority levels with a Select widget.
- Add an "edit" mode with inline editing.
- Sort completed items to the bottom using a Sort node.
- Add a Rate Limit node to the add pipeline.