Onboarding Checklist Rails Component
A compact setup guide that turns progress into momentum. Feed it a JSON object, show completed and loading states automatically, and choose whether the expanded checklist overlays the page or joins the document flow.
Installation
1. Stimulus Controller Setup
The controller renders task states from JSON and manages each expansion mode. Tailwind handles the visual transitions, with the browser’s native Web Animations API reserved for measured heights and dynamic completion particles.
This code is available to Pro users only.
// Hey curious person!
// You seem to be sneaking around the code...
// I hope you're enjoying the components!
// Have a great day!
class SecretMessage {
constructor() {
this.message = "Thanks for checking out Rails Blocks!";
this.compliment = "You're awesome!";
}
reveal() {
console.log(this.message);
return this.compliment;
}
}
const secret = new SecretMessage();
secret.reveal();
2. Floating UI Installation
Overlay modes use the same portaled positioning foundation as the dropdown and popover components.
pin "@floating-ui/dom", to: "https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.7.6/+esm"
npm install @floating-ui/dom
yarn add @floating-ui/dom
3. Number Flow Installation
The progress percentage uses Number Flow for smooth, direction-aware digit transitions.
pin "number-flow", to: "https://esm.sh/number-flow"
pin "number-flow/group", to: "https://esm.sh/number-flow/group"
npm install number-flow
yarn add number-flow
Examples
Overlay below
Opens the checklist beneath its trigger without shifting the surrounding layout.
Your quick start
A few small wins, then you’re flying.
This code is available to Pro users only.
<!-- Hey curious person! -->
<!-- You seem to be sneaking around the code... -->
<!-- I hope you're enjoying the components! -->
<!-- Have a great day! -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secret Message from Rails Blocks</title>
<style>
.secret-message {
font-family: 'Comic Sans MS', cursive;
text-align: center;
padding: 2rem;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.wiggle { animation: wiggle 0.5s ease-in-out infinite; }
@keyframes wiggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(1deg); }
75% { transform: rotate(-1deg); }
}
</style>
</head>
<body>
<div class="secret-message">
<h1>🎉 Hello, Code Detective! 🕵️</h1>
<p>Thanks for checking out Rails Blocks!</p>
<p>You're clearly someone who pays attention to details.</p>
<p>That's exactly the kind of developer we love!</p>
<div class="cta-section">
<button class="awesome-btn wiggle">You're awesome!</button>
<p><small>Seriously, keep being curious! 🚀</small></p>
</div>
<footer>
<p>Built with ❤️ by the Rails Blocks team</p>
<p>Now go build something amazing!</p>
</footer>
</div>
<script>
console.log("🎊 Bonus points for opening the console!");
console.log("Keep exploring and happy coding! 💻");
</script>
</body>
</html>
Overlay above
Opens the checklist above its trigger when the component sits near the bottom of a page or viewport.
Your quick start
A few small wins, then you’re flying.
This code is available to Pro users only.
<!-- Hey curious person! -->
<!-- You seem to be sneaking around the code... -->
<!-- I hope you're enjoying the components! -->
<!-- Have a great day! -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secret Message from Rails Blocks</title>
<style>
.secret-message {
font-family: 'Comic Sans MS', cursive;
text-align: center;
padding: 2rem;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.wiggle { animation: wiggle 0.5s ease-in-out infinite; }
@keyframes wiggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(1deg); }
75% { transform: rotate(-1deg); }
}
</style>
</head>
<body>
<div class="secret-message">
<h1>🎉 Hello, Code Detective! 🕵️</h1>
<p>Thanks for checking out Rails Blocks!</p>
<p>You're clearly someone who pays attention to details.</p>
<p>That's exactly the kind of developer we love!</p>
<div class="cta-section">
<button class="awesome-btn wiggle">You're awesome!</button>
<p><small>Seriously, keep being curious! 🚀</small></p>
</div>
<footer>
<p>Built with ❤️ by the Rails Blocks team</p>
<p>Now go build something amazing!</p>
</footer>
</div>
<script>
console.log("🎊 Bonus points for opening the console!");
console.log("Keep exploring and happy coding! 💻");
</script>
</body>
</html>
Inline flow
Expands the checklist in the document flow and moves the content below it.
Your quick start
A few small wins, then you’re flying.
This code is available to Pro users only.
<!-- Hey curious person! -->
<!-- You seem to be sneaking around the code... -->
<!-- I hope you're enjoying the components! -->
<!-- Have a great day! -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Secret Message from Rails Blocks</title>
<style>
.secret-message {
font-family: 'Comic Sans MS', cursive;
text-align: center;
padding: 2rem;
background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
border-radius: 10px;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
.wiggle { animation: wiggle 0.5s ease-in-out infinite; }
@keyframes wiggle {
0%, 100% { transform: rotate(0deg); }
25% { transform: rotate(1deg); }
75% { transform: rotate(-1deg); }
}
</style>
</head>
<body>
<div class="secret-message">
<h1>🎉 Hello, Code Detective! 🕵️</h1>
<p>Thanks for checking out Rails Blocks!</p>
<p>You're clearly someone who pays attention to details.</p>
<p>That's exactly the kind of developer we love!</p>
<div class="cta-section">
<button class="awesome-btn wiggle">You're awesome!</button>
<p><small>Seriously, keep being curious! 🚀</small></p>
</div>
<footer>
<p>Built with ❤️ by the Rails Blocks team</p>
<p>Now go build something amazing!</p>
</footer>
</div>
<script>
console.log("🎊 Bonus points for opening the console!");
console.log("Keep exploring and happy coding! 💻");
</script>
</body>
</html>
JSON input
Pass one JSON object through data-onboarding-checklist-data-value. Build task statuses from your Rails records so a refresh always restores the real state. Supported states are pending, loading, completed, and skipped.
When url is present, the entire task row becomes a link and the action appears as its destination label. Add target: "_blank" only when the destination should open separately. Navigating does not complete the task automatically—send the updated status from your real activation event so checklist progress reflects value reached, not clicks.
Set data-onboarding-checklist-mode-value to overlay-above or overlay-below for a portaled Floating UI panel with no layout shift or clipping, or inline for an animated expansion that moves the content below it. The legacy overlay value remains an alias for overlay-above.
Checklist options
checklist_idscopes live updates when more than one checklist is present.title,panel_title,panel_description, andcompleted_summarycontrol the copy.update_eventchanges the defaultonboarding-checklist:updateevent name.completed_eventchanges the emittedonboarding-checklist:completedevent.dismiss_on_completeremoves the checklist after its success animation. Tune it withdismiss_delayanddismiss_duration.
Task options
id,title,description,status, andactiondefine the task.urlandtargetmake the row a link;focusmakes it reveal and focus an element already on the page.action_eventmakes the row dispatch an app-defined event for modals, drawers, or any custom workflow. Addclose_on_action: truewhen the checklist should collapse first.completion_eventandcompletion_selectorcomplete a task from real app behavior. Optionalcompletion_url,completion_method, andcompletion_payloadpersist it.optional: trueadds Skip/Restore controls. Skipped tasks leave the progress denominator; useskip_url,skip_method, andskip_payloadto persist that choice.iconacceptsuser,folder,users,sparkles,bookmark,bell,link,keyboard, orchrome. For your own SVG, passicon_pathoricon_paths, plus optionalicon_view_boxandicon_fill.
Real app integration
Derive initial state in Rails
Keep the server authoritative. A helper or presenter can build the JSON from your records and return nothing once every required task is complete, preventing a completed checklist from flashing back on refresh.
checklist = {
checklist_id: "account-setup",
dismiss_on_complete: true,
tasks: [
{
id: "profile",
title: "Complete your profile",
status: current_user.profile_complete? ? "completed" : "pending",
action: "Open",
url: edit_profile_path,
icon: "user"
},
{
id: "invite",
title: "Invite a teammate",
status: current_account.members.many? ? "completed" : "pending",
action: "Invite",
action_event: "team:invite",
icon: "users"
}
]
}
required_tasks = checklist[:tasks].reject { |task| task[:status] == "skipped" }
checklist = nil if required_tasks.any? && required_tasks.all? { |task| task[:status] == "completed" }
Update tasks after Turbo or in-page actions
Dispatch one generic event after the app reaches the milestone. Updates merge by task ID, so the server can change status, copy, URL, or action without replacing the whole checklist. Pass replace: true with a full tasks array when replacing all task data is simpler.
document.dispatchEvent(new CustomEvent("onboarding-checklist:update", {
detail: {
checklist_id: "account-setup",
task: {
id: "invite",
status: "completed",
action: "Manage",
url: "/account/members"
}
}
}))
Connect custom task actions
Use action_event when a task should open an app-owned modal or start a workflow instead of navigating. The event bubbles from the checklist and includes both camelCase and snake_case task IDs.
<div
data-controller="onboarding-checklist invite-modal"
data-action="team:invite->invite-modal#open"
data-onboarding-checklist-data-value="..."
>
...
</div>