I am a fan of documenting processes. They decrease barriers to working on a task, save time, and prevent mistakes. If done well, recurring tasks can be done without much thinking. Let’s see if I can find a checklist plugin to improve my workflow.
For years, I have done weekly and monthly reviews. Since the related tasks are defined clearly, I finish the reviews rather quickly these days and can even do so early in the morning or late at night when my mind is sleepy.
Well-documented steps also make it simpler to spot tasks that could be automated or further improved, as well as identify those that are not yet clear enough.
Years ago, I built an internal knowledge base using WordPress. It contains static information, principles, guidelines, and processes.
Compared to tools like Trello, the information in the knowledge base is self-hosted and, therefore, privacy-safe and controllable. However, features like checklists are missing. For now, to-do lists in the knowledge base are just plain lists without checkboxes.
I am now looking into converting the existing lists into interactive to-do lists or checklists and was wondering if there was a WordPress plugin for that. I documented my findings below for your benefit.
Checklist requirements
Without a to-do list with checkboxes, I have to remember the last item I did when being interrupted, and the team cannot really share the process if multiple members are involved.
My ideal checkbox plugin would include the following features:
- The checklist is a single block, editable in the block editor, not on a separate admin page
- Tick off any item that was completed – by anyone on the team
- Choose between personal lists – checked items are only visible to this particular user – and ones visible and editable by everyone
- Allow users to reset all related checkboxes so the process can be repeated
- Works with indented items
- Bonus: Convert an existing bullet list into an interactive checklist
- Bonus: Show the process, e.g., “4 / 5 done”, which might be motivating for the people involved
Checklist plugins on wordpress.org
I started by testing the most promising-looking plugins using the “checklist” tag on wordpress.org.
Well, I ended up not installing any of them for two reasons:
- The purpose was different, e.g., a pre-publish checklist for editors
- They were outdated
Here are a few of the latter if you are still interested:
- Checklist in Post, not updated for 6 years
- Checklist, not updated for 5 years and seems to use a SaaS service to manage checklists
- Frontend Checklist, not updated for 5 years and closed due to a security issue
To-do list plugins
Next, I looked for “to-do list“ plugins on the repo. After digging a bit, I found some interesting looking candidates, though no perfect fit.
Todo List Block

The Todo List Block plugin by Rich Tabor looked promising initially, until I noticed that it only works in the backend. It can be used as a pre-publish checklist, for planning content, etc.
Todo Block

Todo Block by David Towoju has the most promising wp.org profile for my purpose.
After watching the video on the plugin page and reading through the reviews, it became clear that it lacks the features I was looking for.
One can check a box, but the state is not stored. So, when the page is reloaded, the checkbox state is reverted.
I also did a web search, but this is all there was for to-do list plugins.
Using form plugins
My search brought me to form plugins.
I found tutorials for GravityKit and Formidable Forms to create frontend to-do lists. However, they didn’t come in the form of a block, and the list/form needed to be created separately.
Custom Code (Updated)
A year after I originally wrote this post, reader pointed me to a helpful thread on the WordPress.org forums that outlines a lightweight, privacy‑friendly workaround.
The idea is to convert a standard list into a checklist on the frontend and persist the state locally in the browser via localStorage: no plugin, no login, and no server storage.
Source: https://wordpress.org/support/topic/i-need-a-todo-list-but-cant-find-a-solution/
- What you get
- Click to mark an item as done (strike-through), click again to undo.
- State is saved across reloads and days (per device/browser).
- Works anywhere you can place a Custom HTML block.
- Accessible keyboard toggle (Enter/Space).
- Limitations
- The state is not shared between users or devices. It’s personal to that browser.
- If you need shared lists across users.
Here’s the minimal version for a single checklist.
HTML:
<ul id="todo">
<li data-id="a">Item A</li>
<li data-id="b">Item B</li>
<li data-id="c">Item C</li>
</ul>Code language: HTML, XML (xml)
CSS:
<style>
#todo li { cursor: pointer; }
#todo li.done { text-decoration: line-through; opacity: .7; }
</style>Code language: HTML, XML (xml)
JavaScript
<script>
document.addEventListener('DOMContentLoaded', () => {
const KEY = 'todo-state-v1';
let saved = {};
try {
saved = JSON.parse(localStorage.getItem(KEY)) || {};
} catch (e) {}
document.querySelectorAll('#todo li').forEach(li => {
const id = li.dataset.id || li.textContent.trim();
const setState = (done) => {
li.classList.toggle('done', done);
li.setAttribute('aria-checked', done ? 'true' : 'false');
if (done) saved[id] = true; else delete saved[id];
localStorage.setItem(KEY, JSON.stringify(saved));
};
// Accessibility
li.setAttribute('role', 'checkbox');
li.setAttribute('tabindex', '0');
// Initial state
setState(Boolean(saved[id]));
// Interactions
li.addEventListener('click', () => setState(!li.classList.contains('done')));
li.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setState(!li.classList.contains('done'));
}
});
});
});
</script>Code language: HTML, XML (xml)
If you want more than one checklist on the same page, use a class for each list and give each list its own storage key. This keeps the lists independent.
Here is the full script in one block.
<ul class="shopping-list">
<li>Option One</li>
<li>Option Two</li>
<li>Option Three</li>
<li>Option Four</li>
</ul>
<ul class="shopping-list">
<li>Second List Option One</li>
<li>Second List Option Two</li>
<li>Second List Option Three</li>
</ul>
<style>
.shopping-list li { cursor: pointer; transition: .2s; }
.shopping-list li.done { text-decoration: line-through; opacity: .7; }
.shopping-list li:hover { color: #0073aa; }
</style>
<script>
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.shopping-list').forEach((list, listIndex) => {
const storageKey = `checkedItems_${listIndex}`;
let savedState = {};
try {
savedState = JSON.parse(localStorage.getItem(storageKey)) || {};
} catch (e) {}
list.querySelectorAll('li').forEach((item, itemIndex) => {
const setState = (done) => {
item.classList.toggle('done', done);
savedState[itemIndex] = done;
localStorage.setItem(storageKey, JSON.stringify(savedState));
};
// Initial
setState(Boolean(savedState[itemIndex]));
// Toggle
item.addEventListener('click', () => {
const done = !item.classList.contains('done');
setState(done);
});
});
});
});
</script>Code language: HTML, XML (xml)
I added the script as a Custom HTML block in the bordered section below so you can test it.
- Option One
- Option Two
- Option Three
- Option Four
- Second List Option One
- Second List Option Two
- Second List Option Three
Notes
- While this code works perfectly, it lacks a kind of ”checkbox“ style. Right now, it is not intuitiv to click on items in the list.
- You can paste the HTML/CSS/JS into a single “Custom HTML” block. If you prefer to keep JS centralized, enqueue the script via a small mu‑plugin or a code manager like WPCode/Code Snippets.
- If you reorder list items later, consider adding stable data-id attributes so the stored state doesn’t mismatch.
Again, credit where it’s due — the original idea and snippets came from the community thread on WordPress.org: https://wordpress.org/support/topic/i-need-a-todo-list-but-cant-find-a-solution/
Conclusion
While I am a proficient WordPress developer, I’ve hoped to find a solution that is ready to be used. I am undecided if I will invest the time to build one now, and I am grateful for any tips on ready-to-use WordPress plugins. Maybe I’ve missed an existing solution.