Hey!
I’ve developed a solution for a common challenge in Softr: creating a multi-step form for editing user accounts. This is particularly useful when dealing with multiple fields, as a single long form might overwhelm users.
The Challenge
Softr doesn’t have a built-in option for multi-step forms when editing user profiles, making it challenging for users to fill out all fields at once.
The Solution
To overcome this limitation, I created a workaround using four separate “User Profile” blocks. Here’s how it works:
- Split your form fields into four blocks:
user-accounts1
user-accounts2
user-accounts3
user-accounts4
- Use custom code to navigate between these blocks, creating a multi-step form experience.
Implementation
1. HTML (Add to the Custom Code block in the Header section):
html
<div class="area-editing-steps">
<span class="editing-steps">Step 1</span>
<a class="editing-steps-previous">« Previous</a>
</div>
2. CSS (Add to the Custom Code block in the Header section):
css
<style>
.area-editing-steps {
display: flex;
align-items: center;
justify-content: center;
position: relative;
width: 33%;
margin: 0 auto;
}
.editing-steps {
color: #9796FC;
font-size: 26px;
font-weight: bold;
}
.editing-steps-previous {
color: #9796FC;
font-size: 26px;
margin-left: 10px;
cursor: pointer;
transition: color 0.3s ease, transform 0.3s ease;
}
.editing-steps-previous:hover {
color: #6e6cfb;
transform: scale(1.1);
}
.editing-steps-previous:active {
color: #4d4ccb;
transform: scale(0.9);
}
#user-accounts1, #user-accounts2, #user-accounts3, #user-accounts4 {
display: none;
}
#user-accounts1.active, #user-accounts2.active, #user-accounts3.active, #user-accounts4.active {
display: block;
}
</style>
3. JavaScript (Add to the Custom Code block in the Footer section):
javascript
document.addEventListener('DOMContentLoaded', () => {
let currentStep = 1; // Start at step 1
const maxSteps = 4; // Total number of steps
// Update step text and visibility
const updateStepText = () => {
document.querySelector('.editing-steps').textContent = `Step ${currentStep}`;
updateVisibility();
};
// Show relevant step and hide others
const updateVisibility = () => {
for (let i = 1; i <= maxSteps; i++) {
const section = document.querySelector(`#user-accounts${i}`);
if (section) {
section.classList.toggle('active', i === currentStep); // Toggle visibility based on current step
}
}
};
// Add event listener to the "Next" button only
const addClickListenerToNextButton = () => {
const nextButton = document.querySelector('.MuiButton-containedPrimary'); // Select the "Next" button by class
if (nextButton) {
nextButton.removeEventListener('click', handleButtonClick); // Remove previous listeners to avoid duplication
nextButton.addEventListener('click', handleButtonClick); // Add new listener for the "Next" button
}
};
// Handle "Next" button click
const handleButtonClick = (event) => {
const button = event.currentTarget;
const parentForm = button.closest('form');
if (parentForm) {
setTimeout(() => {
if (!checkForErrors(parentForm)) { // If no errors, proceed to the next step
if (currentStep < maxSteps) {
currentStep++; // Move to the next step
updateStepText(); // Update text and visibility for the new step
}
} else {
console.debug('Error detected, staying on the same step'); // If error found, log and stay on the current step
}
}, 500); // Wait 500ms for any error to appear
}
};
// Check for form errors
const checkForErrors = (parentForm) => {
const errorElement = parentForm.querySelector('.form-error-text'); // Look for error messages
return errorElement !== null; // Return true if errors are present
};
// "Previous" button listener to go back a step
document.querySelector('.area-editing-steps').addEventListener('click', (event) => {
if (event.target.classList.contains('editing-steps-previous')) {
if (currentStep > 1) { // Only allow moving back if not on the first step
currentStep--; // Go back one step
updateStepText(); // Update text and visibility for the previous step
}
}
});
// Initialize the step display and text on page load
updateStepText();
// Observer for dynamically added "Next" buttons
const observer = new MutationObserver(addClickListenerToNextButton);
observer.observe(document.body, { childList: true, subtree: true }); // Listen for changes in the DOM to dynamically add listeners
// Add event listener to the "Next" button present on page load
addClickListenerToNextButton();
});
How It Works
- HTML creates a navigation area displaying the current step and a “Previous” button.
- CSS styles the navigation and manages step visibility.
- JavaScript handles step logic, checks for form errors, and moves to the next step only if no errors are present.
Feel free to adapt the code to your needs. If you have any questions or suggestions, let me know. Happy coding!