├── app ├── favicon.ico ├── globals.css ├── layout.js ├── student-onboarding │ └── page.jsx └── page.js ├── jsconfig.json ├── next.config.js ├── postcss.config.js ├── redux ├── Providers.jsx ├── store.js └── slices │ └── onboardingStudentsSlice.js ├── components ├── MultiStepForm │ ├── Steps.jsx │ ├── Step.jsx │ ├── StepForms │ │ ├── FormConfirmation.jsx │ │ ├── TechnicalSkillsForm.jsx │ │ ├── PreferredLanguageForm.jsx │ │ ├── EducationBackgroundForm.jsx │ │ ├── ProgrammingExperienceForm.jsx │ │ └── PersonalInfoForm.jsx │ └── StepForm.jsx └── FormInputs │ ├── SelectInput.jsx │ ├── TextAreaInput.jsx │ ├── TextInput.jsx │ ├── QuillEditor.jsx │ ├── ToggleInput.jsx │ ├── NavButtons.jsx │ ├── ImageInput.jsx │ ├── SubmitButton.jsx │ └── ArrayItemsInput.jsx ├── .gitignore ├── tailwind.config.js ├── public ├── vercel.svg └── next.svg ├── package.json └── README.md /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MUKE-coder/multi-step-form-with-redux-and-nextjs/HEAD/app/favicon.ico -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {} 3 | 4 | module.exports = nextConfig 5 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | min-height: 100vh; 7 | } 8 | -------------------------------------------------------------------------------- /redux/Providers.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from "react"; 3 | import { Provider } from "react-redux"; 4 | import { store } from "./store"; 5 | 6 | export default function Providers({ children }) { 7 | return {children}; 8 | } 9 | -------------------------------------------------------------------------------- /redux/store.js: -------------------------------------------------------------------------------- 1 | import onboardingStudentsSlice from "./slices/onboardingStudentsSlice"; 2 | 3 | const { configureStore } = require("@reduxjs/toolkit"); 4 | 5 | //create a store and give it slices 6 | export const store = configureStore({ 7 | reducer: { 8 | onboarding: onboardingStudentsSlice, 9 | }, 10 | }); 11 | 12 | //Store =whole state 13 | -------------------------------------------------------------------------------- /components/MultiStepForm/Steps.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Step from "./Step"; 3 | 4 | export default function Steps({ steps }) { 5 | return ( 6 |
7 | {steps.map((step, i) => { 8 | return ; 9 | })} 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /app/layout.js: -------------------------------------------------------------------------------- 1 | import { Inter } from "next/font/google"; 2 | import "./globals.css"; 3 | import Providers from "@/redux/Providers"; 4 | 5 | const inter = Inter({ subsets: ["latin"] }); 6 | 7 | export const metadata = { 8 | title: "Create Next App", 9 | description: "Generated by create next app", 10 | }; 11 | 12 | export default function RootLayout({ children }) { 13 | return ( 14 | 15 | 16 | {children} 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 5 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))", 12 | "gradient-conic": 13 | "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", 14 | }, 15 | }, 16 | }, 17 | plugins: [require("@tailwindcss/forms")], 18 | }; 19 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "onboarding-students", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@reduxjs/toolkit": "^2.0.1", 13 | "lucide-react": "^0.309.0", 14 | "next": "14.0.4", 15 | "react": "^18", 16 | "react-dom": "^18", 17 | "react-hook-form": "^7.49.3", 18 | "react-hot-toast": "^2.4.1", 19 | "react-redux": "^9.1.0", 20 | "redux": "^5.0.1" 21 | }, 22 | "devDependencies": { 23 | "@tailwindcss/forms": "^0.5.7", 24 | "autoprefixer": "^10.0.1", 25 | "postcss": "^8", 26 | "tailwindcss": "^3.3.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /redux/slices/onboardingStudentsSlice.js: -------------------------------------------------------------------------------- 1 | // Create a slice 2 | 3 | const { createSlice } = require("@reduxjs/toolkit"); 4 | 5 | const initialState = { 6 | currentStep: 1, 7 | formData: {}, 8 | }; 9 | const onboardingStudentsSlice = createSlice({ 10 | name: "onboarding", 11 | initialState, 12 | reducers: { 13 | setCurrentStep: (state, action) => { 14 | state.currentStep = action.payload; 15 | }, 16 | updateFormData: (state, action) => { 17 | state.formData = { 18 | ...state.formData, 19 | ...action.payload, 20 | }; 21 | }, 22 | }, 23 | }); 24 | export const { setCurrentStep, updateFormData } = 25 | onboardingStudentsSlice.actions; 26 | export default onboardingStudentsSlice.reducer; 27 | -------------------------------------------------------------------------------- /components/MultiStepForm/Step.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from "react"; 3 | import { useSelector } from "react-redux"; 4 | 5 | export default function Step({ step }) { 6 | const { number, title } = step; 7 | const currentStep = useSelector((store) => store.onboarding.currentStep); 8 | return ( 9 |
10 |
15 | {number} 16 |
17 |
18 |

Step {number}

19 |

{title}

20 |
21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /components/MultiStepForm/StepForms/FormConfirmation.jsx: -------------------------------------------------------------------------------- 1 | import NavButtons from "@/components/FormInputs/NavButtons"; 2 | import React from "react"; 3 | import { useSelector } from "react-redux"; 4 | 5 | export default function FormConfirmation() { 6 | const formData = useSelector((store) => store.onboarding.formData); 7 | async function processData(data) { 8 | console.log(formData); 9 | } 10 | return ( 11 |
12 |
13 |
14 |
15 | Confirm and Submit Data 16 |
17 |

Confirm if this the Data that you filled

18 |
19 |
20 | 21 |
{JSON.stringify(formData, null, 2)}
22 |
23 |
24 | 25 | 26 | 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /components/FormInputs/SelectInput.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export default function SelectInput({ 4 | label, 5 | name, 6 | register, 7 | className = "sm:col-span-2", 8 | options = [], 9 | multiple = false, 10 | }) { 11 | return ( 12 |
13 | 19 |
20 | 35 |
36 |
37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /components/MultiStepForm/StepForm.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useSelector } from "react-redux"; 4 | import EducationBackgroundForm from "./StepForms/EducationBackgroundForm"; 5 | import FormConfirmation from "./StepForms/FormConfirmation"; 6 | import PersonalInfoForm from "./StepForms/PersonalInfoForm"; 7 | import PreferredLanguageForm from "./StepForms/PreferredLanguageForm"; 8 | import ProgrammingExperienceForm from "./StepForms/ProgrammingExperienceForm"; 9 | import TechnicalSkillsForm from "./StepForms/TechnicalSkillsForm"; 10 | 11 | export default function StepForm() { 12 | const currentStep = useSelector((store) => store.onboarding.currentStep); 13 | function renderFormByStep(step) { 14 | if (step === 1) { 15 | return ; 16 | } else if (step === 2) { 17 | return ; 18 | } else if (step === 3) { 19 | return ; 20 | } else if (step === 4) { 21 | return ; 22 | } else if (step === 5) { 23 | return ; 24 | } else if (step === 6) { 25 | return ; 26 | } 27 | } 28 | return <>{renderFormByStep(currentStep)}; 29 | } 30 | -------------------------------------------------------------------------------- /components/FormInputs/TextAreaInput.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | export default function TextareaInput({ 3 | label, 4 | name, 5 | register, 6 | errors, 7 | isRequired = true, 8 | className = "sm:col-span-2", 9 | }) { 10 | return ( 11 |
12 | 18 |
19 |