Skip to content

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

  1. Go to WP-Nexus > Apps and click Create App.
  2. Name it Todo App.
  3. Go to the Tables tab and click Add Table.
  4. Name the table todos.
  5. Add these columns:
Column NameTypeDescription
titlevarchar(255)The todo text
completedtinyint0 = not done, 1 = done
user_idintThe WordPress user ID who owns this todo
created_atdatetimeWhen it was created
  1. 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

  1. Go to the Components tab and click Add Component.
  2. 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_out

Step 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)
                  → Output

Node 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")
    → Result

This 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)                ← Delete

Style 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 == 0

So 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)
        → Output

Set 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)
      → Output

The 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

  1. Save the component.
  2. Create a shortcode in the Shortcodes tab.
  3. Add the shortcode to a WordPress page.
  4. Visit the page while logged in.
  5. Add a few todos.
  6. Mark one as complete and verify it shows as completed.
  7. Delete a todo and verify it disappears.
  8. Log out and verify the "Please log in" message appears.
  9. 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.