Skip to main content

Quickstart (Frontend)

This guide will help you hit the ground running with Privy using the privy-browser client, authenticating users directly with Privy via Sign In With Ethereum.

info

Unsure about which client you should be using? Refer to Picking a client to understand which client is right for you.

What are we building? โ€‹

We're going to walk through a simple app that lets you log in with MetaMask and associate some user data with your wallet off-chain using Privy. This demonstrates how to use Privy without a backend using our privy-browser client.

Say you want to take on some personally-identifiable information (PII) such as a user's date of birth as well as some information to customize a user's UX experience, such as their favorite color. By the end of this demo, you'll be able to show them a profile page with their favorite color:

This app corresponds to the Frontend-only architecture described in Picking a client:

;

0. Prerequisitesโ€‹

If you want to follow along, you'll need:

  • A Privy account. If you don't have one, get one here.
  • The MetaMask browser extension (or other Ethereum wallet).
  • Npm installed.

You can also see the quickstart running live, and see the full code on Github.

To get started, go ahead and create a brand new Next.js app:

npx create-next-app privy-quickstart

1. Installationโ€‹

Once you cd into the privy-quickstart directory, install the privy-browser package with npm:

npm install @privy-io/privy-browser

2. Authenticate the clientโ€‹

In order to allow the end-user to write data to Privy, we need to be able to authenticate said user. Otherwise, anyone could overwrite an end-user's name and birthdate.

Privy supports multiple options to authenticate the browser client, which you can read more about the Authentication guide. In this demo, we'll focus on the case in which you run no app backend, using Privy's implementation of the Sign-In-With-Ethereum (SIWE) standard to authenticate your user with Privy.

tip

SIWE is an EIP that provides a way to authenticate end-users directly using their wallets.

The privy-browser client comes with a wrapper for conveniently managing SIWE sessions and associated state. To see how this works, replace pages/index.js with the following:

import {PrivyClient, SiweSession} from '@privy-io/privy-browser';
import {useEffect, useState} from 'react';
import Head from 'next/head';

// Initialize the Privy client.
// provider here refers to the browser's Ethereum wallet provider, in this case Metamask.
const provider = typeof window !== 'undefined' ? window.ethereum : null;
const session = new SiweSession(process.env.NEXT_PUBLIC_PRIVY_API_KEY, provider);
const client = new PrivyClient({
session: session,
});

Notice that our app takes the Privy api key via process.env.NEXT_PUBLIC_PRIVY_API_KEY. In order to get your API key, go to the Privy Console > Settings and copy the api key into a .env.local file in the root of the app with the following contents:

NEXT_PUBLIC_PRIVY_API_KEY="<your api key from the console here>"

Now, append this code snippet to the end of pages/index.js:

// .. Previous code above ..
export default function Home() {
// Use React's useState hook to keep track of the signed in Ethereum address.
const [address, setAddress] = useState(null);

// Connect to a MetaMask wallet.
const connectToWallet = async () => {
try {
const {ethereum} = window;

if (!ethereum) {
alert('Please install MetaMask for this demo: https://metamask.io/');
return;
}

if (!(await session.isAuthenticated())) {
await session.authenticate();
}
const address = await session.address();
setAddress(address);
} catch (error) {
console.error(error);
}
};

return (
<>
<Head>
<title>Privy Quickstart</title>
</Head>
<div>To get started, connect with MetaMask!</div>
<button onClick={connectToWallet}>Connect Wallet</button>
</>
);
}

(See the full source of pages/index.js.)

There are 3 points to highlight in the code snippet:

  1. We use React's useState hook to allow the DOM to access to user's wallet address:
const [address, setAddress] = useState(null);
  1. connectToWallet calls Privy's convenient session.authenticate() function in order to generate the SIWE message and initialize the session.
  2. Finally, we set up a very simple DOM in the return call to hook up connectToWallet to a button.

Because this is a tutorial on using Privy and not CSS, we'll go ahead and copy-paste a ready-made CSS file into styles/global.css. Now when we run npm run dev we can see the SIWE session in action!

3. Store data with Privyโ€‹

With authentication set up, let's now think about what user data we want to take on. The goal of the app is to onboard users and set up a profile page for them, customized to their favorite color. We'll set up 3 fields, corresponding to the following pieces of user data:

  1. Name: The user's name (a piece of personally-identifiable info, i.e. PII data).
  2. Date of Birth: Another piece of sensitive data.
  3. Favorite Color: A toy example of user data used to personalize the user's experience.

The Privy console Fields page provides an easy way to create these fields and add descriptions for each field: .

We'll go ahead and set up 3 fields via the console, first-name, date-of-birth, and favorite-color in the console, corresponding to each of the pieces of data described above.

Once we have the fields set up with Privy, we'll then add corresponding React hooks to have access to the data in the DOM. In our Home() function, we'll initialize state variables for each of the pieces of data using React's setState hook.

// .. Previous code above ..

export default function Home() {
// Use React's useState hook to keep track of the signed in Ethereum address.
const [address, setAddress] = useState(null);
const [firstName, setFirstName] = useState("");
const [dateOfBirth, setDateOfBirth] = useState("");
const [favoriteColor, setFavoriteColor] = useState("");

// ... continued below ...

(See index.js up to this step for context)

Now add a putUserData() function that we can call to persist these pieces of state to the appropriate fields with Privy:

// .. rest of Home() function above ..
// Write the user's name, date-of-birth, and favorite color to Privy.
const putUserData = async () => {
const [name, birthday, color] = await client.put(address, [
{
field: 'first-name',
value: firstName,
},
{
field: 'date-of-birth',
value: dateOfBirth,
},
{
field: 'favorite-color',
value: favoriteColor,
},
]);
setFirstName(name.text());
setDateOfBirth(birthday.text());
setFavoriteColor(color.text());
};

(See index.js up to this step for context)

The core of this function is the Privy client's put(userID, ..) call. It takes in the wallet address as the userId and a field and value.

We then update the React app state with setFirstName(..) to sync app state with Privy's persisted state. All data is end-to-end encrypted once it leaves the browser and Privy never sees unencrypted data. The decryption is done in the browser and retrieved via FieldInstance.text().

Finally, replace the JSX returned by Home() to conditionally show an input form if the user has signed in (i.e. the wallet address variable is non-null). We'll also add a React hook to make in order to check and update the address (as done in Step 2) based on whether the user has signed in:

// .. Previous code above ..
// Update address if page is refreshed.
const updateAddress = async () => {
const address = await session.address();
setAddress(address);
};
useEffect(() => {
updateAddress();
}, []);

return (
<>
<Head>
<title>Privy Quickstart</title>
</Head>
{!address && (
<>
<div>To get started, connect with MetaMask!</div>
<button onClick={connectToWallet}>Connect Wallet</button>
</>
)}
{address && (
<div className="container">
<h1>
Hey {firstName ? firstName : address.substring(0, 5) + "..."} ๐Ÿ‘‹
</h1>
<div>
<div className="inputForm">
<label htmlFor="name">Name</label>
<input
id="name"
onChange={(event) => {
setFirstName(event.target.value);
}}
value={firstName}
placeholder={address.substring(0, 5) + "..."}
/>
<label htmlFor="dob">Date of Birth</label>
<input
id="Date Of Birth"
onChange={(event) => {
setDateOfBirth(event.target.value);
}}
value={dateOfBirth}
/>
<label htmlFor="color">Favorite Color</label>
<input
onChange={(event) => {
setFavoriteColor(event.target.value);
}}
value={favoriteColor}
/>
</div>
</div>
<div>
<button style={{ fontSize: "1.6rem" }} onClick={putUserData}>
Save with Privy
</button>
</div>
</div>
)}
</>
);
}

At the end of this step, we've:

  • Set up React state to store user data locally
  • Created a data schema with 3 user fields with Privy.
  • Added a putUserData method to persist data to Privy and we can see the following input form, ready to take on user data:

4. Retrieve data from Privyโ€‹

In the previous step, we successfully stored data with Privy. But we haven't actually done anything with it! In this step, we retrieve the data from Privy and customize the UX to the user's preferences.

Start by adding a getUserData method. We can put it right under putUserData to keep things together:

// .. putUserData() above ..

// Get user data from Privy.
const getUserData = async () => {
try {
if (!address) return;

// Fetch user's name and favorite color from Privy
const [firstName, dateOfBirth, favoriteColor] = await client.get(address, [
'first-name',
'date-of-birth',
'favorite-color',
]);
setFirstName(firstName?.text());
setDateOfBirth(dateOfBirth?.text());
setFavoriteColor(favoriteColor?.text());
} catch (error) {
console.error(error);
}
};

// Get the user data from Privy whenever the wallet address is set.
useEffect(() => {
getUserData();
}, [address]);

// .. Continued below ..

(See index.js up to this step for context)

The core of this function is the Privy client's get(userID, ..) call. Much like the put() call, we use wallet address as the userId and specify the field names that we want to retrieve. We hook it up to a React hook to pull data whenever the address is updated. Finally, we add a hook to customize the background color to the user's preferences.

// .. Previous code above ..
// Set background to user's favorite color.
useEffect(() => {
if (!favoriteColor) return;
document.body.style = `background: ${favoriteColor};`;
}, [favoriteColor]);

(See the full source at index.js for context).

...and Voila, run npm run dev and we now have a personalized app that safely takes in user PII data (e.g. name and date of birth) as well as user preferences (e.g. favorite color) and customizes the user experience based on preferences!

5. Explore next stepsโ€‹

It's really that simple - with just a few lines of code, Privy handles your user data for you. But there's so much more that Privy can do!