User Profile Tutorial

Implement a user profile card in your app in just a few minutes with our Gravatar API.

Profile as a Service

Imagine you sign up for a new service and go to your profile, only to find it already fully set up for you. Your avatar, bio, and social media links are all there.

The Gravatar API allows you to populate a complete user profile for your users based on Gravatar data the moment they sign up and provide their email address. All while you have full control over front-end design and user data. Our API allows you to save user data in your database for any other use or functionality in your app.

Benefits of implementing a profile based Gravatar:
  • Saves you the time and effort of building user profile form interactions, database setups, and addressing data security concerns.
  • Solves the cold-start problem of empty user profiles, making new users feel at home instantly.
  • User profile data stays current. Profile data can be synced with Gravatar anytime.
Advantages for Your Users:
  • Users can bring their profile and online identity without repeatedly filling out data forms.
  • When users join your service, their user profile is ready to go, with their familiar Gravatar data.
  • Users can easily edit their profile data via Gravatar with just one click, or can create a new Gravatar account without leaving your platform. Their changes will be synced globally.

We would like to show you how to build a profile page with Gravatar in just a few steps. See the tutorial below for a full-fledged user profile for the web (React / NodeJS):


Web (React / NodeJS)

Demo

Here is what we will build:

A full user profile card component, with an avatar, bio, location, verified social media links and more. This is how it will look like:

As you saw in the demo above, we will also integrate a search field into the prototype view, so you can preview and test your user profile card with any email that has a Gravatar profile attached.

Let’s start!


Step 1: Setup a dev environment and your Gravatar API Key

Get your Gravatar API Key

Signup for a Gravatar account:

If you have no Gravatar Account yet, signup here, it takes just a minute.

Get an API Key:

Now get your API Key. Go to Gravatar’s developer dashboard and create an application. Afterwards create an API key. Be aware that we show the key just once. Copy and store it at a safe place for now.

Pre-requisites

Before starting, ensure you have Node v20 or greater installed.

Installation

For the sake of simplicity, we will use the package create-gravatar-react-profile-example to create the application skeleton. Open a terminal window and run the following command:

npx create-gravatar-react-profile-example <project-name>

Replace <project-name> with a name for your application.

You might be prompted to install the package. Just choose Yes (Y).

Afterwards you will be prompted to choose between creating a complete or a starter application.

? What type of template do you want to create? (Use arrow keys)
❯ Complete application [ Ready to play with ]
  Starter application [ To follow along with the tutorial ]

For this tutorial, we will use the “Starter application”.

Once the project creation is completed, a new folder will have been created with the project name you chose. Open that folder in your preferred code editor and proceed to the Step 2:


Step 2: Hook up the Gravatar API Setting up the backend endpoint

In this step, we will set up an endpoint in our Node.js backend to fetch Gravatar profile data.

  1. Navigate to the src/server directory and open the index.js file. Here we will:
    • Add a new route to handle requests to fetch Gravatar profile information based on an email address.
    • The Gravatar API expects the email address to be hashed with the SHA-256 algorithm. To address that, we will use the “crypto” library built-in in the most recent Node versions.
    • To get complete profile information, the API request must be authenticated. For that, we need to send an “Authorization” token in the request’s header. This token will be your Gravatar API key.
  2. Let’s add the /api/profile/:identifier endpoint:
    Replace the comment // [ Insert the route to fetch Gravatar data here ] with the following code snippet:
app.get( '/api/profile/:identifier', async ( req, res ) => {
    const { identifier } = req.params;
 
    try {
        const hash = crypto.createHash( 'sha256' ).update( identifier.trim().toLowerCase() ).digest( 'hex' );
 
        const response = await axios.get( `https://api.gravatar.com/v3/profiles/${ hash }`, {
            headers: {
                Authorization: `Bearer ${ process.env.API_KEY }`,
            },
        } );
 
        res.json( response.data );
    } catch ( error ) {
        res.status( error.response ? error.response.status : 500 ).json( {
            error: error.message
        } );
    }
} );
  1. Open the .env file in the root directory and paste the API key you generated in the Gravatar Developer dashboard:
API_KEY=[your_api_key_here]

Note: You should not add your API key directly in the code nor send it to your remote repository. For that, we will use a .env file to store the key.

A .env file is a file to store environment variables. The server then reads this file to make the variables available during the code execution.

It is a good practice to add .env files to your .gitignore. This way, you don’t accidentally upload sensitive information to your remote servers.

  1. Open a terminal window, navigate to the folder where you installed the project, and start the application by running the following command:
npm start

The application should start on http://localhost:3000

You can now test the backend endpoint by opening it on your browser and providing an email address associated with a Gravatar account. E.g.:

http://localhost:3000/api/profile/sarahthompson@fork.do

You should see the Gravatar profile information in JSON format.


Step 3: Design Your Profile Page: Creating the GravatarCard Component

The design of your profile card will depend on your service and what is relevant to your users. The look and feel should align with your app’s design.

For this example, we’ll use a fully customized design of a profile card component for a fictional service, to display Gravatar profile data. We will use:

display_name: The user’s display name that appears on their profile.
Example: "Alex Morgan"

profile_url: The full URL to the user’s Gravatar profile.
Example: "https://gravatar.com/example"

avatar_url: The URL to the user’s avatar image, if set.
Example: "https://0.gravatar.com/avatar/33252cd1f33526af53580fcb1736172f06e6716f32afdd1be19ec3096d15dea5"

location: The user’s geographical location.
Example: "New York, USA"

description: A short biography or description about the user found on their profile.
Example: "I like playing hide and seek."

job_title: The user’s current job title.
Example: "Landscape Architect"

company: The name of the company where the user is employed.
Example: "ACME Corp"

verified_accounts: An array of verified accounts the user has added to their profile. The number of verified accounts displayed is limited to a maximum of 4 in unauthenticated requests.

pronunciation: A phonetic guide to pronouncing the user’s name.
Example: "Al-ex Mor-gan"

pronouns: The pronouns the user prefers to use.
Example: "She/They"

You can obviously choose your own for your projects. Here is an example for a set of profile data the Gravatar API provides: Developer Console | Documentation

For data inspiration, you also can take a look at our public Gravatar profiles. All data that is shown on our Gravatar profiles also could become part of your profile design. Keep in mind, that our API might offer even more than you can see on certain profiles.

Now let’s build this profile card as a simple React component:

  1. Open the file src/client/components/gravatar-card/index.js and add the following code:
import React, { useEffect, useState } from 'react';
import './style.scss'
 
export default function GravatarCard( { email } ) {
    const [ gravatarData, setGravatarData ] = useState( null );
 
    const loadGravatarData = async () => {
        if ( ! email ) {
            return;
        }
 
        const response = await fetch( '/api/profile/' + email );
 
        if ( ! response.ok ) {
            return;
        }
 
        const data = await response.json();
        setGravatarData( data );
    }
 
    useEffect( () => {
        loadGravatarData();
    }, [ email ] );
 
    if ( ! gravatarData ) {
        return null;
    }
 
    return (
        <div className="gravatar-card">
            <img src={ gravatarData.avatar_url + '?size=256' } className="gravatar-card__avatar" />
            <h1 className="gravatar-card__name">{ gravatarData.display_name }</h1>
            <div className="gravatar-card__meta">
                <div>
                    { gravatarData.job_title && (
                        <span>
                            { gravatarData.job_title }
                            { gravatarData.company && ( ', ' ) }
                        </span>
                    ) }
                    { gravatarData.company && (
                        <span>{ gravatarData.company }</span>
                    ) }
                </div>
                <div className="gravatar-card__meta-personal">
                    { gravatarData.pronunciation && (
                        <>
                            <span>{ gravatarData.pronunciation }</span>
                            { gravatarData.pronouns && ( <span>·</span> ) }
                        </>
                    ) }
                    { gravatarData.pronouns && (
                        <>
                            <span>{ gravatarData.pronouns }</span>
                            { gravatarData.location && ( <span>·</span> ) }
                        </>
                    ) }
                    { gravatarData.location && (
                        <span>{ gravatarData.location }</span>
                    ) }
                </div>
            </div>
            <p className="gravatar-card__description">{ gravatarData.description }</p>
            <div className="gravatar-card__network">
                <>
                    <a href={ gravatarData.profile_url }>
                        <img src="https://secure.gravatar.com/icons/gravatar.svg" />
                    </a>
                    { gravatarData.verified_accounts.slice( 0, 3 ).map( acc =>
                        <a key={ acc.service_label } href={ acc.url }>
                            <img src={ acc.service_icon } alt={ acc.service_label } />
                        </a>
                    ) }
                </>
            </div>
        </div>
    )
}

This component expects an email property and will use it to fetch the Gravatar profile data. We will provide the email property in a moment.

  1. Let’s add some styles. We have some prepared for you, but obviously, you could make that card look any way you want. Open the file src/client/components/gravatar-card/style.scss and add the following CSS:

.gravatar-card {
    background: linear-gradient(159.68deg, #C1AF99 -35.31%, #EEEAE6 73.65%);
    border-radius: 8px;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    padding: 24px;
    width: 400px;
}
 
.gravatar-card__avatar {
    border-radius: 50%;
    height: 120px;
    margin-bottom: 16px;
    width: 120px;
}
 
.gravatar-card__name {
    font-size: 40px;
    font-weight: 700;
    line-height: 48px;
    letter-spacing: 0.25px;
    margin: 0 0 4px;
}
 
.gravatar-card__meta {
    color: rgba(16, 21, 23, 0.6);
    font-size: 14px;
    font-weight: 400;
    line-height: 21px;
    margin-bottom: 16px;
}
 
.gravatar-card__meta-personal {
    display: flex;
    gap: 8px;
}
 
.gravatar-card__description {
    display: -webkit-box;
    font-size: 14px;
    font-weight: 400;
    line-height: 21px;
    margin: 0 0 16px;
    overflow: hidden;
    text-overflow: ellipsis;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}
 
.gravatar-card__network {
    display: flex;
    gap: 4px;
}
 
.gravatar-card__network img {
    display: flex;
    height: 32px;
    width: 32px;
}
 
.gravatar-card__error {
    padding: 20px;
    color: #CC1818;
    background: #ffe6e6;
    border-radius: 4px;
}
  1. Now, open the file src/client/index.js, include the GravatarCard component and set the value of the email property. We use this as placeholder for now, till we implement the search. The code should look like this:
import React from 'react';
import { createRoot } from 'react-dom/client';
import GravatarCard from './components/gravatar-card';
import './style.scss';
 
const App = () => (
    <div className="gravatar-example">
        <GravatarCard email="sarahthompson@fork.do " />
    </div>
);
 
const container = document.getElementById( 'root' );
const root = createRoot( container );
root.render( <App /> );
  1. We can test this now. Open a new terminal window and run the command:
npm run dev

This command will build the client files and watch for the changes we will add in the next steps.

Note: Apart from the above command, you still need to have “npm start” running in a different terminal window to ensure everything will work as expected. You can now open http://localhost:3000/, and you should see something similar to:


Step 4: Creating a Gravatar Search Component

For preview and debugging purposes, let’s integrate a simple search field. This will allow you to search for any Gravatar profile using the corresponding email address. This way, you can preview your profile page implementation with a variety of different profiles and data sets.

Let’s create the React component to search Gravatar profiles. The GravatarSearch function is a simple custom React component that wraps the input field and the search button. The “onSearchProfile” is the function called when you hit the search button. It is defined in the GravatarLoader component and passed down as a property to this component.

  1. Open the file src/client/components/gravatar-search/index.js and add:
import React from 'react';
import './style.scss'
 
export default function GravatarSearch( { email, onSearchProfile, onSetEmail } ) {
    return (
        <div className="gravatar-search">
            <input placeholder="Enter email address" type="text" value={ email } onChange={ ( e ) => onSetEmail( e.target.value ) } />
            <button onClick={ onSearchProfile }>Search profile</button>
        </div>
    )
}
  1. Now, open the file src/client/components/gravatar-search/style.scss and add some styles:

.gravatar-search {
    display: flex;
    gap: 16px;
    margin-bottom: 40px;
    width: 100%;
}
 
.gravatar-search input {
    border: 1px solid rgba(220, 220, 220, 1);
    border-radius: 4px;
    flex-grow: 1;
    font-family: inherit;
    font-size: 14px;
    padding: 9px 12px 9px 12px;
}
 
.gravatar-search button {
    background: rgba(29, 79, 196, 1);
    border-radius: 4px;
    border: none;
    color: #fff;
    cursor: pointer;
    font-family: inherit;
    font-size: 16px;
    font-weight: 600;
    letter-spacing: -0.32px;
    line-height: 22px;
    padding: 10px 16px;
}
  1. Open the file src/client/index.js and include the <GravatarSearch /> component:

import React from 'react';
import { createRoot } from 'react-dom/client';
import GravatarCard from './components/gravatar-card';
import GravatarSearch from './components/gravatar-search';
import './style.scss';
 
const App = () => (
    <div className="gravatar-example">
        <h1>Hello, Gravatar!</h1>
        <GravatarSearch />
        <GravatarCard email="sarahthompson@fork.do" />
    </div>
);
 
const container = document.getElementById( 'root' );
const root = createRoot( container );
root.render( <App /> );

If you go back to http://localhost:3000, you should see the search component, but it isn’t doing anything. Let’s hook it up with the API.

  1. First, let’s adjust some small things in the GravatarCard component. We’ll add an error handling, a loading state and remove the fetch logic from this component.  Open src/client/components/gravatar-card/index.js and adjust the code to be similar to:
import React from 'react';
import './style.scss'
 
export default function GravatarCard( { gravatarData, hasError, isLoading } ) {
    if ( hasError ) {
        return (
            <div className="gravatar-card__error">
                Gravatar profile not found!
            </div>
        )
    }
 
    if ( isLoading ) {
        return <div>Loading...</div>
    }
 
    if ( ! gravatarData ) {
        return null;
    }
 
    return (
        <div className="gravatar-card">
            <img src={ gravatarData.avatar_url + '?size=256' } className="gravatar-card__avatar" />
            <h1 className="gravatar-card__name">{ gravatarData.display_name }</h1>
            <div className="gravatar-card__meta">
                <div>
                    { gravatarData.job_title && (
                        <span>
                            { gravatarData.job_title }
                            { gravatarData.company && ( ', ' ) }
                        </span>
                    ) }
                    { gravatarData.company && (
                        <span>{ gravatarData.company }</span>
                    ) }
                </div>
                <div className="gravatar-card__meta-personal">
                    { gravatarData.pronunciation && (
                        <>
                            <span>{ gravatarData.pronunciation }</span>
                            { gravatarData.pronouns && ( <span>·</span> ) }
                        </>
                    ) }
                    { gravatarData.pronouns && (
                        <>
                            <span>{ gravatarData.pronouns }</span>
                            { gravatarData.location && ( <span>·</span> ) }
                        </>
                    ) }
                    { gravatarData.location && (
                        <span>{ gravatarData.location }</span>
                    ) }
                </div>
            </div>
            <p className="gravatar-card__description">{ gravatarData.description }</p>
            <div className="gravatar-card__network">
                <>
                    <a href={ gravatarData.profile_url }>
                        <img src="https://secure.gravatar.com/icons/gravatar.svg" />
                    </a>
                    { gravatarData.verified_accounts.slice( 0, 3 ).map( acc =>
                        <a key={ acc.service_label } href={ acc.url }>
                            <img src={ acc.service_icon } alt={ acc.service_label } />
                        </a>
                    ) }
                </>
            </div>
        </div>
    )
}
  1. Let’s create the component that will manage the state of our application and also hold the searching logic. Open the file src/client/components/gravatar-loader/index.js and add the following code:

import React, { useState } from 'react';
import GravatarCard from '../gravatar-card';
import GravatarSearch from '../gravatar-search';
import './style.scss'
 
export default function GravatarLoader() {
    const [ email, setEmail ] = useState( '' );
    const [ gravatarData, setGravatarData ] = useState( null );
    const [ isLoading, setIsLoading ] = useState( false );
    const [ hasError, setHasError ] = useState( false );
 
    const onSearchProfile = async () => {
        if ( ! email ) {
            return;
        }
 
        setGravatarData( null );
        setHasError( false );
        setIsLoading( true );
 
        const response = await fetch( '/api/profile/' + email );
 
        if ( ! response.ok ) {
            setIsLoading( false );
            setHasError( true );
            return;
        }
 
        const data = await response.json();
        setGravatarData( data );
        setIsLoading( false );
    }
 
    return (
        <div className="gravatar-loader">
            <GravatarSearch email={ email } onSearchProfile={ onSearchProfile } onSetEmail={ setEmail } />
            <GravatarCard gravatarData={ gravatarData } hasError={ hasError } isLoading={ isLoading } />
        </div>
    )
}
  1. Open the file src/client/components/gravatar-loader/style.scss and add the necessary styles:
.gravatar-loader {
    width: 400px;
    margin: 0 auto;
    padding: 40px;
    color: #101517;
}
  1. Since the <GravatarCard /> now lives inside the <GravatarLoader /> component, we need to replace it in the main application file. Open src/client/index.js, remove the <GravatarCard /> and add  the fresh new <GravatarLoader />. Remember to import the `gravatar-loader` at the top of the file. The code should look like this:
import React from 'react';
import { createRoot } from 'react-dom/client';
import GravatarLoader from "./components/gravatar-loader";
import './style.scss';
 
const App = () => (
    <div className="gravatar-example">
        <h1>Hello, Gravatar!</h1>
        <GravatarLoader />
    </div>
);
 
const container = document.getElementById( 'root' );
const root = createRoot( container );
root.render( <App /> );

You can now go back to http://localhost:3000 and use the search component to search and display Gravatar profiles!

🥳!


Step 5: Creating a skeleton loading effect

Now you have a working application, let’s make it even nicer by adding a skeleton loading effect.

  1. Create a file skeleton.js under src/client/components/gravatar-card/ with the following content:
import React from 'react';
 
export default function GravatarSkeleton() {
    return (
        <div className="gravatar-card skeleton">
            <span className="skeleton__avatar" />
            <span className="skeleton__name" />
            <span className="skeleton__meta" />
            <span className="skeleton__meta" />
            <span className="skeleton__description" />
            <div className="skeleton__network">
                <span className="skeleton__network-item" />
                <span className="skeleton__network-item" />
                <span className="skeleton__network-item" />
                <span className="skeleton__network-item" />
            </div>
        </div>
    )
}
  1. Open the file src/client/components/gravatar-card/style.scss and add these styles at the end of the file:
@keyframes skeleton-loading {
    0% {
        background-color: hsl(200, 20%, 90%);
    }
    50% {
        background-color: hsl(200, 20%, 95%);
    }
    100% {
        background-color: hsl(200, 20%, 90%);
    }
}
 
.skeleton > * {
    animation: skeleton-loading 2s ease-in-out infinite;
}
 
.skeleton__avatar {
    border-radius: 50%;
    height: 120px;
    margin-bottom: 16px;
    width: 120px;
}
 
.skeleton__name {
    height: 48px;
    width: 300px;
    border-radius: 4px;
    margin-bottom: 4px;
}
 
.skeleton__meta {
    height: 21px;
    width: 240px;
    border-radius: 4px;
    margin-bottom: 1px;
}
 
.skeleton__description {
    height: 42px;
    width: 352px;
    border-radius: 4px;
    margin: 16px 0;
}
 
.skeleton__network {
    animation: unset;
    display: flex;
    gap: 4px;
}
 
.skeleton__network-item {
    animation: skeleton-loading 2s ease-in-out infinite;
    border-radius: 4px;
    height: 32px;
    width: 32px;
}
  1. Now, open the file src/client/components/gravatar-card/index.js and replace
if ( isLoading ) {
    return <div>Loading...</div>
}

with

if ( isLoading ) {
    return <GravatarSkeleton />
}

Remember to import the GravatarSkeleton at the top of the file:

import React from 'react';
import GravatarSkeleton from './skeleton';
import './style.scss'

Now, go back to http://localhost:3000/ and try searching for some Gravatar profiles again.

You should see a nice skeleton loading effect:


Step 6: Adding a link and some legalese

Don’t forget to provide a link to gravatar.com/profile on your profile page so people have an easy way to edit their Gravatar profile.
-> And yes, there should be an easier way for users to do this! Good news: Something new is coming soon!

Use of our free APIs is governed by these Guidelines for Responsible Use. Documentation is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License


Step 7: That’s it

We hope this was helpful.

Please keep in mind, we showed here just a very simply profile card. You could build a full fledged profile design if you would like. There is lots of data to play around with in our API.

If you have any questions or suggestions, please let us know anytime.

If you ship something based on Gravatar, please share the link with us — it would make our day! We can’t wait to see what you build with it.


Provide a link to gravatar.com/profile and let your users know how to edit their Gravatar profile. Use of our free APIs is governed by these Guidelines for Responsible Use. Documentation is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.


Last updated on:

Blog at WordPress.com.