webdevpuneet.com

Mobile Edit Profile Screen — Free HTML CSS JS Snippet

Mobile Edit Profile Screen · Mobile · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="mpe-phone">
  <div class="mpe-screen">
    <div class="mpe-status"><span>9:41</span><span class="mpe-batt"><i></i></span></div>
    <header class="mpe-head">
      <button class="mpe-cancel" id="mpeCancel">Cancel</button>
      <h1>Edit Profile</h1>
      <button class="mpe-save" id="mpeSave" disabled>Save</button>
    </header>

    <form class="mpe-form" id="mpeForm">
      <div class="mpe-avatar-wrap">
        <div class="mpe-avatar" id="mpeAvatar">JM</div>
        <label class="mpe-avatar-edit" for="mpeFile">&#128247;</label>
        <input type="file" id="mpeFile" accept="image/*" hidden>
      </div>

      <div class="mpe-field">
        <label>Full name</label>
        <input type="text" id="mpeName" value="Jordan Mireles" required>
        <p class="mpe-err" id="mpeNameErr" hidden>Name is required.</p>
      </div>

      <div class="mpe-field">
        <label>Username</label>
        <div class="mpe-prefix-input">
          <span>@</span>
          <input type="text" id="mpeUsername" value="jordan.m">
        </div>
        <p class="mpe-hint" id="mpeUserHint">Available</p>
      </div>

      <div class="mpe-field">
        <label>Bio</label>
        <textarea id="mpeBio" maxlength="140" rows="3">Product designer. Coffee, trail running, and side projects.</textarea>
        <p class="mpe-count"><span id="mpeBioCount">62</span>/140</p>
      </div>

      <div class="mpe-field">
        <label>Email</label>
        <input type="email" id="mpeEmail" value="[email protected]" required>
        <p class="mpe-err" id="mpeEmailErr" hidden>Enter a valid email address.</p>
      </div>

      <div class="mpe-field">
        <label>Phone</label>
        <input type="tel" id="mpePhone" value="+1 (555) 019-4482">
      </div>
    </form>

    <p class="mpe-toast" id="mpeToast" hidden>&#10003; Profile updated</p>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Dirty-state tracking: Save stays disabled until a field genuinely differs from its starting value
Real avatar upload with instant local preview via FileReader.readAsDataURL
Label-wrapped hidden file input — no custom click-forwarding JavaScript needed
Live field validation on every keystroke, not only on submit attempt
Username availability hint checked and updated as the user types
Live bio character counter respecting a maxlength
Cancel reverts every field to its original snapshot and resyncs all derived UI
Save re-snapshots values on success so the button correctly disables again
Toast confirmation on successful save

About this UI Snippet

Mobile Edit Profile Screen — Dirty-State Save, Avatar Upload & Live Validation

Screenshot of the Mobile Edit Profile Screen snippet rendered live

Editing a profile is different from viewing one — see the mobile profile screen for the read-only counterpart — because the interface needs to track what changed, prevent an accidental save of untouched data, and give feedback while the user is still typing rather than only after they submit. This snippet builds a complete edit-profile form: a real avatar upload with an instant image preview, a Save button that only enables once something has actually changed, a live bio character counter, a username-availability check as you type, and field-level validation errors that don't require a submit attempt to appear.

Dirty-state tracking, not just "form has values"

On load, initialValues snapshots every input and textarea's starting value by element id. Every subsequent input event calls checkDirty(), which compares each field's current value against its snapshot and enables Save only if at least one field genuinely differs from where it started. This is meaningfully different from just checking whether required fields are non-empty — a user who opens the form, types something, and then deletes it back to the original value should not have an enabled Save button implying there's something new to persist, and this implementation gets that right because it compares against the actual starting values, not just presence.

A real avatar upload with FileReader, not a stub

Clicking the small camera badge opens the native file picker (via a <label for="mpeFile"> wrapping a hidden <input type="file">, so no custom click-forwarding JavaScript is needed). Selecting an image reads it with FileReader.readAsDataURL(), and on load sets the avatar's background-image directly from the resulting data URL — a genuine local preview with no upload round-trip required to see the result, exactly like production avatar-picker flows show an immediate preview before the file finishes uploading in the background.

Validation that appears without a submit attempt

Name and email fields validate on every keystroke via the shared input listener rather than only when Save is clicked — an empty name or a malformed email shows its red border and inline error message immediately, then clears the moment the field becomes valid again. validate() is still called again inside the Save handler as the authoritative gate (so a user can't defeat validation by never triggering the live check), but by the time they reach Save, they've typically already seen and fixed any problems.

A username check with instant feedback

The username field checks its trimmed, lowercased value against a small takenUsernames array on every keystroke, flipping a hint line between "Available" (green) and "Username already taken" (red) instantly. In production, replace the local array lookup with a debounced request to a real availability-check endpoint, since checking uniqueness against a live username database cannot be done client-side — but keep the same instant-hint UI pattern once the network response resolves.

Cancel reverts, Save commits

Cancel doesn't just close the screen — it writes every field back to its initialValues snapshot, resets the bio counter and username hint, and re-runs checkDirty(), so backing out of an edit-in-progress cleanly discards unsaved changes without navigating away. Save, once validation passes, re-snapshots initialValues to the newly saved values (so Save becomes disabled again until something new changes) and shows a brief toast confirmation.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA profile edit form renders inside a phone frame with Save disabled since nothing has changed yet.
  2. 2
    Edit a fieldSave enables the moment any field genuinely differs from its starting value.
  3. 3
    Upload an avatarTap the camera badge to pick an image — it previews instantly in the circular avatar via FileReader.
  4. 4
    Watch live validationClear the name field or type an invalid email to see inline errors appear before you try to save.
  5. 5
    Check username availabilityType "admin" or "test" into the username field to see the taken-username hint switch to red.
  6. 6
    Save or CancelSave validates and shows a confirmation toast; Cancel reverts every field back to its original value.

Real-world uses

Common Use Cases

Social and community app profile editing
The direct edit counterpart to a mobile profile screen — the standard "tap Edit" destination from any account view.
Account settings and onboarding completion
Reuse the same avatar-upload and dirty-state-save mechanics for a "complete your profile" step during onboarding.
Marketplace and seller profile management
Sellers editing a public-facing bio and contact details benefit from the same live validation and availability-check pattern for usernames or shop handles.
Teaching dirty-state form patterns
A clear, real-world example of snapshot-and-compare dirty tracking, a pattern that generalizes to any "only enable Save once something changed" form.
Related: Mobile Settings Screen
See the Mobile Settings Screen for a related mobile pattern worth pairing with this profile-editing screen.
Related: Mobile Profile Screen
See the Mobile Profile Screen for the read-only view this edit screen is typically reached from.

Got questions?

Frequently Asked Questions

On load, every input and textarea\u2019s starting value is snapshotted into an initialValues object keyed by element id. On every input event, checkDirty() compares each field\u2019s current value against that snapshot and enables Save only if at least one field genuinely differs — not merely whether required fields are filled in.

No — clicking the camera badge opens the native file picker via a label wrapping a hidden file input, and the selected image is read locally with FileReader.readAsDataURL() to produce an instant preview as the avatar\u2019s background image. No network request is made; a production implementation would additionally upload the file to storage and persist the resulting URL on save.

Live validation gives the user a chance to see and correct a problem (like an empty name or malformed email) while they are still filling out the form, rather than only discovering it after clicking Save. The same validate() function still runs again inside the Save handler as the authoritative check, so validation cannot be bypassed even if the live listener were somehow skipped.

On every keystroke in the username field, the trimmed and lowercased value is checked against a small hardcoded takenUsernames array, and a hint line switches between green "Available" and red "Username already taken" accordingly. In production this local array must be replaced with a debounced request to a real backend availability-check endpoint, since uniqueness cannot be verified without querying the actual user database.

It writes every field\u2019s value back to its initialValues snapshot, resets the derived bio counter and username hint to match, and re-runs checkDirty() (which disables Save again since nothing now differs from the snapshot) — fully discarding any in-progress edits without navigating away from the screen.

Keep the form values in a single state object and a separate initialValues object captured on mount; derive "dirty" as a comparison between the two rather than a separate boolean flag that could drift out of sync. Handle the file input\u2019s change event the same way, reading the selected file with FileReader and storing the resulting data URL in state to drive the avatar\u2019s preview.