Dynamic UI

The Dynamic Widget UI component and Dynamic Embedded Widget UI component both contain a user profile section where the user can see their profile information and update it easily. You can check it out at https://demo.dynamic.xyz/.

Hooks Only (Headless)

This guide will help you to create a completely headless user profile including everything you see in the regular Dynamic Widget UI component/Dynamic Embedded Widget UI component described above.

Prerequisites

Like with all this series of headless guides, “headless” is defined a way to use the Dynamic SDK without the need for Dynamic UI components (i.e. DynamicWidget, DynamicUserProfile). You still need to add the SDK and set up the Dynamic Context Provider (complete the quickstart if you haven’t done so already, or refer to an example app)

Setup

Show the user’s profile based on whether they are logged in or not

How: Check if the user is logged in or not Hook/Component: useIsLoggedIn Notes: We start here assuming you have signup/login implemented already
import { useIsLoggedIn } from "@dynamic-labs/sdk-react-core";

const isLoggedIn = useIsLoggedIn();

return <>{isLoggedIn ? <Profile /> : </Login>}</>

Profile Info

Show user profile information

How: Fetch info from user object on useDynamicContext Hook/Component: useDynamicContext Notes: The format of the user can be found here: userProfile. For working with custom metadata fields and storing additional user data, see User Metadata.
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";

const { user } = useDynamicContext();

return (
  <div className="user-details">
    {user?.firstName && <p>First name: {user.firstName} </p>}
    {user?.email && <p>E-Mail: {user.email} </p>}
    {user?.alias && <p>Alias: {user.alias} </p>}
    {user?.lastName && <p>Last name: {user.lastName} </p>}
    {user?.jobTitle && <p>Job: {user.jobTitle} </p>}
    {user?.phoneNumber && <p>Phone: {user.phoneNumber} </p>}
    {user?.tShirtSize && <p>Tshirt size: {user.tShirtSize} </p>}
    {user?.team && <p>Team: {user.team} </p>}
    {user?.country && <p>Country: {user.country} </p>}
    {user?.username && <p>Username: {user.username} </p>}
    {user?.metadata && Object.keys(user.metadata).length > 0 && (
      <div>
        <h4>Custom Metadata Fields:</h4>
        <p><em>Additional user data and preferences stored in metadata</em></p>
        {Object.entries(user.metadata).map(([key, value]) => (
          <p key={key}><strong>{key}:</strong> {String(value)}</p>
        ))}
      </div>
    )}
  </div>
);

Allow user to update their profile information

How: useUserUpdateRequest hook Hook/Component: useUserUpdateRequest Notes: We include the validation for email updates here. This hook also supports updating custom metadata fields for storing additional user data.
import { useUserUpdateRequest, useOtpVerificationRequest } from "@dynamic-labs/sdk-react-core";
import { useState } from "react";

const { verifyOtp } = useOtpVerificationRequest();
const { updateUser } = useUserUpdateRequest();

const [showUpdateForm, setShowUpdateForm] = useState(false);
const [showVerifyForm, setShowVerifyEmailForm] = useState(false);
const [loading, setLoading] = useState(false);

const updateUserInfoFormSubmit = async (e) => {
   e.preventDefault();
   try {
     setLoading(true);
     // Parse custom field input (format: key:value)
     const customFieldInput = e.target[2].value;
     let metadata = {};
     if (customFieldInput && customFieldInput.includes(':')) {
       const [key, value] = customFieldInput.split(':');
       if (key.trim() && value.trim()) {
         metadata[key.trim()] = value.trim();
       }
     }

     // Call the updateUser function with the new values entered by the user
     const { isEmailVerificationRequired } = await updateUser({
       firstName: e.target[0].value,
       email: e.target[1].value,
       metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
     });
     // If email verification is required, show the email verification form
     if (isEmailVerificationRequired) {
       setShowVerifyEmailForm(true);
     }
   } catch (e) {
     console.log("Error", e);
     setLoading(false);
   } finally {
     setLoading(false);
     setShowUpdateForm(false);
   }
 };

   // Handler for the email verification form submission
   const onVerifyEmailFormSubmit = async (e) => {
     e.preventDefault();
     try {
       setLoading(true);
       const verificationToken = e.target[0].value;
       // Call the verifyEmail function with the entered verification token
       await verifyOtp(verificationToken);
     } catch (e) {
       console.log("Error", e);
       setLoading(false);
     } finally {
       setLoading(false);
       // Hide the email verification form after the process is completed
       setShowVerifyEmailForm(false);
     }
     return false;
   };


return (
    <div>
          {/* Render the profile update form */}
          {showUpdateForm && (
            <div>
              <form onSubmit={updateUserInfoFormSubmit} className="form">
                <div className="form__row">
                  <label className="label" htmlFor="firstName">
                    First-Name
                  </label>
                  <input
                    id="firstName"
                    className="form__input"
                    defaultValue={user.firstName}
                    disabled={loading || showVerifyEmailForm}
                  />
                </div>
                <div className="form__row">
                  <label className="label" htmlFor="email">
                    E-Mail
                  </label>
                  <input
                    type="email"
                    id="email"
                    className="form__input"
                    defaultValue={user.email}
                    disabled={loading || showVerifyEmailForm}
                  />
                </div>
                <div className="form__row">
                  <label className="label" htmlFor="customField">
                    Custom Metadata Field (Key:Value)
                  </label>
                  <input
                    id="customField"
                    className="form__input"
                    placeholder="e.g., favoriteColor:blue, subscriptionTier:premium"
                    disabled={loading || showVerifyEmailForm}
                  />
                  <small className="form__help">
                    Store additional user data like preferences, settings, or custom attributes
                  </small>
                </div>
                <button
                  disabled={loading || showVerifyEmailForm}
                  className="form__button"
                  type="submit"
                >
                  Save
                </button>
              </form>
            </div>
          )}

         {/* Render the email verification form if needed */}
          {showVerifyEmailForm && (
            <form onSubmit={onVerifyEmailFormSubmit} className="form">
              <h6>Verify Email</h6>
              <div className="form__row">
                <label htmlFor="verificationToken">Verification Token</label>
                <input
                  disabled={loading}
                  pattern="^\d{6}$"
                  name="verificationToken"
                />
              </div>
              <button disabled={loading} className="form__button" type="submit">
                Send
              </button>
            </form>
          )}
    </div>
  );

Socials

Show users linked social accounts, allow linking/unlinking

How: useSocialAccounts hook from sdk-react-core Hook/Component: useSocialAccounts Notes: None
import { useSocialAccounts } from "@dynamic-labs/sdk-react-core";
import { SocialIcon } from '@dynamic-labs/iconic';

const Avatar = ({ avatarUrl }) => {
  return (
    <div className="avatar">
      <img src={avatarUrl} alt="avatar" />
    </div>
  );
};


const Icon = ({ provider }) => {
  return (
    <div className="icon-container">
    <SocialIcon name={provider} />
    </div>
  );
};

const UserProfileSocialAccount = ({ provider }) => {
  const {
    linkSocialAccount,
    unlinkSocialAccount,
    isProcessing,
    isLinked,
    getLinkedAccountInformation,
  } = useSocialAccounts();

  const isProviderLinked = isLinked(provider);
  const connectedAccountInfo = getLinkedAccountInformation(provider);

  return (
    <div className="social-account">
      <div className="icon">
        {isProviderLinked ? (
          <Avatar avatarUrl={connectedAccountInfo?.avatar} />
        ) : (
          <Icon provider={provider} />
        )}
      </div>
      <div className="label">
        <p>{connectedAccountInfo?.publicIdentifier ?? provider}</p>
      </div>
      {isProviderLinked ? (
        <button
          onClick={() => unlinkSocialAccount(provider)}
          disabled={isProcessing}
        >
          Disconnect
        </button>
      ) : (
        <button
          onClick={() => linkSocialAccount(provider)}
          disabled={isProcessing}
        >
          Connect
        </button>
      )}
    </div>
  );
};

const Socials = () => {
  const providers = [
    "discord",
    "facebook",
    "github",
    "google",
    "instagram",
    "twitch",
    "twitter",
  ];

  return (
    <div className="social-accounts">
      {providers.map((provider) => (
        <UserProfileSocialAccount key={provider} provider={provider} />
      ))}
    </div>
  );
};

Working with User Metadata

User metadata provides a powerful way to store additional information beyond the standard profile fields. This is perfect for storing user preferences, application state, custom attributes, and integration data.

What is Metadata?

Metadata is a flexible key-value storage system that allows you to:
  • Store user preferences (theme, language, notification settings)
  • Track application state (onboarding progress, feature flags)
  • Store business-specific data (subscription tiers, referral codes)
  • Maintain integration data (external service IDs, webhook configs)

Metadata Examples

// User preferences
metadata: {
  theme: 'dark',
  language: 'en',
  notifications_enabled: true
}

// Application state
metadata: {
  onboarding_completed: true,
  feature_flags: ['beta_features', 'advanced_analytics']
}

// Business data
metadata: {
  subscription_tier: 'premium',
  referral_code: 'USER123',
  last_purchase_date: '2024-01-15'
}

Best Practices

  • Size limit: Metadata is limited to 2KB total
  • Key naming: Use descriptive, lowercase keys with underscores
  • Value types: Supports strings, numbers, booleans, and nested objects
  • Validation: Always validate on your backend before storing
For more detailed information about working with metadata, see the User Metadata documentation.