Gattalraouf/mini-form-builder-code-challenge
A dynamic and accessible drag-and-drop form builder built with Nuxt 3, Vue 3, Tailwind CSS, Element Plus, Pinia and Vuedraggable. Users can build forms visually, edit form fields, and preview or submit form data dynamically.
๐ README.md
๐งฉ Mini Form Builder Code Challenge
A dynamic and accessible drag-and-drop form builder built with Nuxt 3, Vue 3, Tailwind CSS, Element Plus, Pinia and Vuedraggable. Users can build forms visually, edit form fields, and preview or submit form data dynamically.
๐ Setup Instructions
1. ๐ฆ Clone the Repository
git clone https://github.com/Gattalraouf/mini-form-builder-code-challenge.git
cd mini-form-builder-code-challenge2. ๐ Install Dependencies
Make sure you're using Node 18+
npm install
# or
yarn3. ๐งช Run the Dev Server
npm run dev4. ๐งฑ Build for Production
npm run build5. ๐ Preview Production Build
npm run preview๐ Project Overview
| Feature | Description |
|---|---|
| ๐จ Drag-and-Drop Builder | Users drag form fields from a palette into the form area |
| โ๏ธ Field Editor Modal | Click any field to edit label, type, placeholder, etc. |
| ๐ Form Preview Page | Navigate to preview to test out the form inputs |
| โ Validation with Rules | Supports required, email format, min length, etc. |
| โฟ Accessibility First | Keyboard navigation and ARIA support throughout |
| ๐ Form Reset & Submit | Resets values or outputs submitted JSON data to console |
๐งฑ Architecture Choices
1. Vue 3 Composition API + <script setup>
We chose the Composition API for its cleaner, scalable structure, especially for shared logic and reactive states.
2. Nuxt 3
Using Nuxt simplifies routing and SSR readiness, with automatic code-splitting and file-based routing.
3. Pinia (with persistence)
State management for form fields is handled globally with Pinia, allowing real-time sync and drag-drop reordering. pinia-plugin-persistedstate ensures changes persist across reloads.
4. Tailwind CSS
Tailwind enables a utility-first approach to styling, offering responsive, accessible, and customizable design without writing traditional CSS.
5. Element Plus
Provides accessible and customizable UI components like form inputs, checkboxes, validation, and modals.
6. Vuedraggable
Enables intuitive drag-and-drop functionality between the palette and form workspace.
๐ Project Structure
๐ฆ src/
โ
โโโ components/
โ โโโ FieldPalette.vue # Field selection sidebar
โ โโโ FormBuilder.vue # Drag-and-drop workspace
โ โโโ DynamicForm.vue # Form renderer with validation
โ โโโ FormField.vue # Single field item in the form
โ โโโ FieldEditModal.vue # Modal to edit individual field
โ
โโโ pages/
โ โโโ index.vue # Form builder main page
โ โโโ preview.vue # Form preview page
โ
โโโ stores/
โ โโโ formStore.ts # Pinia store for managing form fields
โ
โโโ constants/
โ โโโ formElements.ts # List of available field templates
โ
โโโ types/
โ โโโ formField.ts # TypeScript interface for form fields
โ
โโโ plugins/
โ โโโ element-plus.ts # Plugin to register Element Plus
โ โโโ pinia.ts # Plugin to initialize Pinia with persistence
โ
โโโ app.vue # Main layout๐ฆ Tech Stack
- Vue 3 + Nuxt 3
- Pinia + pinia-plugin-persistedstate
- Element Plus โ UI components and validation
- Vuedraggable โ Drag and drop support
- TypeScript โ Type safety
- Tailwind CSS โ for utility-first responsive UI design.
๐ ๏ธ Development Notes
- The app uses a computed field list from the store.
- Each form field includes a unique id, label, type, value, and accessibility attributes.
- Form preview dynamically renders input components based on their type.
๐ค Example Output
On submit, data is logged as:
{
"Full Name": "Jhon Doe",
"A lot of text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"Email Address": "some@email.com",
"Are you certain?": true,
"Do you want to click here?": false
}On submit, form fields list is logged as:
{
[
{
"id": "short-text-1760381499616",
"name": "short-text",
"label": "Full Name",
"description": "Single-line text input for short responses.",
"type": "text",
"placeholder": "Enter your name here",
"required": true,
"value": "Jhon Doe"
},
{
"id": "short-text-1760381505994",
"name": "short-text",
"label": "Middle Name",
"description": "Single-line text input for short responses.",
"type": "text",
"placeholder": "Enter your middle name here",
"required": false,
"value": ""
},
{
"id": "long-text-1760381507111",
"name": "long-text",
"label": "A lot of text",
"description": "Multi-line text area for longer or formatted responses.",
"type": "textarea",
"placeholder": "Enter detailed information...",
"required": true,
"value": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "
},
{
"id": "long-text-1760381509079",
"name": "long-text",
"label": "Some Long Text",
"description": "Multi-line text area for longer or formatted responses.",
"type": "textarea",
"placeholder": "Enter detailed information...",
"required": false,
"value": ""
},
{
"id": "email-1760381511343",
"name": "email",
"label": "Email Address",
"description": "Email input field that validates email addresses.",
"type": "email",
"placeholder": "Enter your email address",
"required": true,
"value": "some@email.com"
},
{
"id": "email-1760381510108",
"name": "email",
"label": "Secondary Email Address",
"description": "Email input field that validates email addresses.",
"type": "email",
"placeholder": "Enter your email address",
"required": false,
"value": ""
},
{
"id": "checkbox-1760381512862",
"name": "checkbox",
"label": "Are you certain?",
"description": "Simple checkbox input for true/false or yes/no choices.",
"type": "checkbox",
"placeholder": "",
"required": true,
"value": true
},
{
"id": "checkbox-1760381515284",
"name": "checkbox",
"label": "Do you want to click here?",
"description": "Simple checkbox input for true/false or yes/no choices.",
"type": "checkbox",
"placeholder": "",
"required": false,
"value": false
}
]
}