Build User Account Pages with Profile Update Using Django and Next.js

Overview

Modern web apps must allow users to manage their profile—update name, phone, etc.—securely and easily. In this blog post, you’ll learn how to:

✅ Set up an account page in Next.js
✅ Connect it with a Django backend API
✅ Use REST API to fetch and update user info
✅ Handle form submission and validation
✅ Secure endpoints using authentication

Tech Stack

  • Django – Backend REST API using Django Rest Framework (DRF)
  • Next.js 14 – Frontend (App Router)
  • JWT Authentication (or Session-based)
  • Axios for HTTP requests
  • CSS Modules or Tailwind for styling

Folder Structure

/backend (Django)
  └── accounts/
      ├── models.py
      ├── views.py
      ├── serializers.py
      └── urls.py

/frontend (Next.js)
  └── app/account/page.tsx
  └── app/account/EditProfile.tsx
  └── lib/api.ts

1. Django Setup (Backend)

1.1 Extend User Model
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    phone = models.CharField(max_length=15, blank=True, null=True)

1.2 Create Serializer
# accounts/serializers.py
from rest_framework import serializers
from .models import CustomUser

class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = CustomUser
        fields = ['username', 'email', 'first_name', 'last_name', 'phone']

1.3 Create Views
# accounts/views.py
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from .serializers import UserProfileSerializer

class UserProfileView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        serializer = UserProfileSerializer(request.user)
        return Response(serializer.data)

    def put(self, request):
        serializer = UserProfileSerializer(request.user, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

1.4 Add URLs
# accounts/urls.py
from django.urls import path
from .views import UserProfileView

urlpatterns = [
    path("profile/", UserProfileView.as_view(), name="user-profile"),
]

And include this in your project urls.py.

2. Next.js Frontend

2.1 Axios Setup
// lib/api.ts
import axios from "axios";

const api = axios.create({
  baseURL: "http://localhost:8000/api", // your backend base URL
  withCredentials: true, // if using session cookies
});

export default api;

2.2 Account Page (page.tsx)
"use client";
import { useEffect, useState } from "react";
import api from "@/lib/api";
import EditProfile from "./EditProfile";

interface User {
  username: string;
  email: string;
  first_name: string;
  last_name: string;
  phone?: string;
}

export default function AccountPage() {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    api.get("/profile/")
      .then((res) => setUser(res.data))
      .catch(console.error);
  }, []);

  return (
    <div className="account-container">
      <h1>My Account</h1>
      {user ? <EditProfile user={user} /> : <p>Loading...</p>}
    </div>
  );
}

2.3 Edit Profile Form (EditProfile.tsx)
"use client";
import { useState } from "react";
import api from "@/lib/api";

interface Props {
  user: {
    username: string;
    email: string;
    first_name: string;
    last_name: string;
    phone?: string;
  };
}

export default function EditProfile({ user }: Props) {
  const [form, setForm] = useState(user);
  const [status, setStatus] = useState("");

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setForm({ ...form, [e.target.name]: e.target.value });
  };

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      await api.put("/profile/", form);
      setStatus("✅ Profile updated successfully");
    } catch {
      setStatus("❌ Failed to update profile");
    }
  };

  return (
    <form onSubmit={handleSubmit} className="form-profile">
      <input name="first_name" value={form.first_name} onChange={handleChange} placeholder="First Name" />
      <input name="last_name" value={form.last_name} onChange={handleChange} placeholder="Last Name" />
      <input name="phone" value={form.phone || ""} onChange={handleChange} placeholder="Phone" />
      <button type="submit">Save Changes</button>
      {status && <p>{status}</p>}
    </form>
  );
}

3. Authentication Notes

You can use either:

  • JWT Token Auth with Authorization: Bearer <token> header
  • Session-Based Auth (recommended for Next.js SSR pages using cookies)

In both cases:

  • Protect Django views with IsAuthenticated
  • Send auth tokens via cookies or headers from frontend

API Summary

EndpointMethodAuthDescription
/api/profile/GETYes (user)Fetch current user
/api/profile/PUTYes (user)Update user profile

Final Outcome

You now have:

  • A user-friendly account dashboard
  • Secure, real-time profile editing
  • A clean structure ready for extensions (password change, photo upload, etc.)

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top