> Portal Navigation: > > - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version. > - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages). > - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`). > - Top-level index of all portals: https://dev.wix.com/docs/llms.txt > - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt ## Resource: Building Your Own Members Area ## Article: Building Your Own Members Area ## Article Link: https://dev.wix.com/docs/develop-websites/articles/code-tutorials/wix-members/building-your-own-members-area.md ## Article Content: # Velo Tutorial: Building Your Own Members Area
Visit the Velo by Wix website to onboard and continue learning.
>**Note:** > You can now [add a members area to your site](https://support.wix.com/en/article/adding-a-members-area-to-your-site) without using code. You can also [create a page that displays items based on the currently logged-in member](https://support.wix.com/en/article/creating-custom-member-profile-pages) without using code. In this article we demonstrate how to create a profile page for each of your site members. The data for the dynamic profile page is created when a site visitor first logs in to your site. Members can view and update their personal profiles at any time.   >**Note:** > The functionality for logging members in and out of your site only works fully when viewing your published site. You will not be able to fully test your member profile pages in preview mode. To create our member profile pages we build: * A members collection * A profile page * An update page * A login page This article assumes you are familiar with the following concepts: * [Database collections](https://support.wix.com/en/article/about-database-collections) * [Collection permissions](https://support.wix.com/en/article/about-database-collection-permissions) * [Page permissions](https://support.wix.com/en/article/applying-page-permissions) * [Dynamic item pages](https://support.wix.com/en/article/setting-up-a-dynamic-list-page) * [Dataset Modes](https://support.wix.com/en/article/working-with-dynamic-page-dataset-settings) * [Data Binding](https://support.wix.com/en/article/about-data-binding-and-datasets) ### Members Collection We begin by creating a collection named **Members**, which is where we store all of the member data. Each item in the collection is another member. Because we want to restrict access to members so they can only view and update their own items, we set the permissions for the **Members** collection to **Custom Use** and choose the following: * Who can read content from this collection? - **Site member author** * Who can create content for this collection? - **Site member** * Who can update content from this collection? - **Site member author** * Who can delete content from this collection? - **Admin** The collection can contain whatever member data you want. In this example, we use the following fields: |Field Name |Field ID |Type | |---|---|---| |Title|title|Text |First Name|firstName|Text |Last Name|lastName|Text |Email|email|Text |Phone|phone|Text |About|about|Text ### Profile Page Next, we create a page to display member profiles. It is a dynamic item page connected to the **Members** collection. Because we are creating a dynamic item page, we need to choose a field to add to the URL that uniquely identifies each member. Here we use each member's automatically generated IDs to uniquely identify them.  The URL looks like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/91f39f0c-8c58-4db6-a1e9-4c658c9723c1/2017/12/04/72d0bc8d-b3ee-4b9f-a8eb-717a27785b36.jpg)
Because we're only using this page to display data, we set the dynamic dataset's mode to **Read-only**. We also set the page's permissions to **Members Only** using the **Permissions** tab of the **Page Settings** so that only members who are logged in can reach the page. The page itself is designed with elements for displaying the information in a member's profile. In our example, that means text elements that are connected through the page's dynamic dataset to the fields in the **Members** collection. Finally, we add a button element. When we create the update page, we will connect the link of the button to navigate to the update page for the current member. When members check their profile pages, they will see something like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2019/02/26/67dae2e0-381c-4e50-b149-61d95b767a2a.png)
### Update Page The update page is very similar to the profile page. It is also a dynamic item page connected to the **Members** collection. It also uses the members' IDs as the unique identifying field. However, because we can't have two pages with the same URL in our site, we add **Update** to the URL so it looks like this: 
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/91f39f0c-8c58-4db6-a1e9-4c658c9723c1/2017/12/04/4d4c8e20-c73e-4a39-8088-802bd42c5c76.jpg)
Because we're displaying data on this page and allowing members to edit their data, we set the dynamic dataset's mode to **Read & Write**. Once again we set the page's permissions to **Members Only** using the **Permissions** tab of the **Page Settings** so that only members who are logged in can reach the page. This time the page is designed with elements for displaying and editing the information in a member's profile. In our example, we'll use a dropdown element, some text input elements, and a text box element. All these elements are connected through the page's dynamic dataset to the fields in the **Members** collection. Finally, we add a button element for submitting edited data. We connect the submit button using the **Connect to CMS** button as follows: | Field | Value | | ------------------------ | ---------------- | | Choose a dataset | Members Item | | Click action connects to | Profile Page | When members see their update pages, they will see something like this:
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2019/02/26/49208fe8-9e4e-4009-a15e-07599033f187.png)
Now that we've created the update page, we can return to the profile page to link the update button. We connect the update button using the **Connect to CMS** button as follows: | Field | Value | | ------------------------ | ---------------- | | Choose a dataset | Members Item | | Click action connects to | Update Page | ### Login Page In our example we have a dedicated page for members to use for logging into the site. However, you can set this page as your homepage, add the contents of this page to any page, or [show them on all pages](https://support.wix.com/en/article/showing-an-element-on-all-pages-6533570).  >**Note:** > Because we have a dedicated login page, we'll be adding the code below to this page. However, if you're showing the elements on all pages, you need to add the code below to **masterPage.js**. The design of our login page is very simple. It contains two buttons. The first button is used for either logging in or logging out, depending on whether the current member is already logged in or not. The second button is used to navigate to a member's profile page. It is only shown for members who are already logged in.
![](https://d2x3xhvgiqkx42.cloudfront.net/12345678-1234-1234-1234-1234567890ab/dfff8cbb-ddcb-4376-8cae-3fedb2dd4458/2017/09/04/4f007dec-b68d-4c07-9a88-02b300b2cb3a.png)
#### Code The code on our login page consists of four parts: * Imports for the APIs used in the rest of the code * An onReady event handler for setting up the page when it loads * A login button onClick event handler for either: * logging in a member and possibly creating a new item in the **Members** collection if it is a first-time login * logging out a member who is already logged in * A profile button onClick event handler for navigating members to their profile pages * * * Let's take a look at the code piece by piece to understand what it's doing. **Import APIs and Declare Variables** ```javascript import { currentMember, authentication } from 'wix-members-frontend'; import wixData from 'wix-data'; import wixLocationFrontend from 'wix-location-frontend'; let memberId, memberEmail; ``` **Lines 1-3**: First, we import all the APIs we need for our example: [wix-members-frontend](https://www.wix.com/velo/reference/wix-members): For getting information about the current member and for logging members in and out. [wix-data](https://www.wix.com/velo/reference/wix-data.html): For querying and inserting into the Members collection. [wix-location-frontend](https://www.wix.com/velo/reference/wix-location.html): For navigating to other pages **Line 5**: We declare 2 global variables, `memberId` and `memberEmail`. **Check if the Current Member is Logged In** ```javascript async function checkIfLoggedIn(){ const thisMember = await currentMember.getMember(); return thisMember ? true : false; } ``` **Lines 1-2**: We add an [async](https://dev.wix.com/docs/develop-websites/articles/coding-with-velo/java-script-velo/working-with-promises.md) function to check if the current member is logged in, and await the current member. **Lines 3-4**: We return the current member. If we can get the current member, `thisMember` is `true`, and if not, then `thisMember` is `false`. **Set Up the 'login/logout' Buttons** ```javascript $w.onReady( () => { if (wixWindowFrontend.rendering.env === "browser") { checkIfLoggedIn().then(res => { if (res === true) { $w("#loginButton").label = "Logout"; $w("#profileButton").show(); } else { $w("#loginButton").label = "Login"; $w("#profileButton").hide(); } }) ``` **Lines 1-3**: In the `onReady()` function, we set the window rendering environment to ‘browser’ to prevent the code in the `onReady()` function from running twice. We then call the `checkIfLoggedIn()` function to see if the current member is logged in. **Lines 4-6**: If the current member is logged in, we label the button ‘Logout’, and show the profile button. **Lines 7-11**: If the current member is not logged in, we label the button ‘Login', and hide the profile button. **Authenticate the Member on Log In** ```javascript authentication.onLogin(async (member) => { const loggedInMember = await member.getMember(); memberId = loggedInMember._id; memberEmail = loggedInMember.loginEmail; return wixData.query("Members") .eq("_id", memberId) .find() .then((results) => { if (results.Members.length === 0) { const toInsert = { "_id": memberId, "email": memberEmail }; wixData.insert("Members", toInsert) .catch( (err) => { console.log(err); } ); } }) .catch( (err) => { console.log(err); }); }); } }); ``` **Lines 1-2**: Still in the `onReady()` function, we add an async function authenticating the member when they log in, and await the current member data. **Lines 3-4**: We set the current member ID and email, returned from the current member data to variables. **Lines 5-25**: We search the ‘Members’ data collection for the current member’s ID. If no results are found, then we insert a new item in the ‘Members’ collection for the new member, with their ID and email. **When the Login Button is Clicked** ```javascript export async function loginButton_click(event) { if(await checkIfLoggedIn() === true) { authentication.logout(); console.log('Member is logged out.') $w("#loginButton").label = "Login"; $w("#profileButton").hide(); } else { authentication.promptLogin({"mode": "login"}) .then(() => { console.log('Member is logged in.'); $w("#loginButton").label = "Logout"; $w("#profileButton").show(); }) .catch((error) => { console.error(error); }); } } ``` **Lines 1-6**: We add an async event handler to the login button. When a member clicks the login button, the function first checks if the member is logged in. If the member is logged in, the button’s label already shows ‘Logout’ from the logic in the `onReady()` function above. So when the member clicks the (logout) button, they are logged out, the button label changes to 'Login', and the profile button is hidden. **Lines 7-18**: If the member is not logged in, the button’s label already shows ‘Login’ from the logic in the `onReady()` function above. So when the member clicks the (login) button, they are prompted to log in, the button label then changes to 'Logout', and the profile button is shown. **When the Profile Button is Clicked** ```javascript export async function profileButton_click(event) { let loggedInMember = await currentMember.getMember(); memberId = loggedInMember._id; wixLocationFrontend.to(`/items/${memberId}`); } ``` **Lines 1-2**: We add an async event handler to the profile button. When a member clicks the profile button, the function first gets the current member data. **Lines 3-5**: We set the current member ID, returned from the current member data to a variable. We pass the variable to the `wix-location-frontend.to()` function, sending members to their personal profile page. * * * **Full Example Code** Here is the complete code for this example: ```javascript import { currentMember, authentication } from 'wix-members-frontend'; import wixData from 'wix-data'; import wixLocationFrontend from 'wix-location-frontend'; let memberId, memberEmail; async function checkIfLoggedIn(){ const thisMember = await currentMember.getMember(); return thisMember ? true : false; } $w.onReady( () => { if (wixWindowFrontend.rendering.env === "browser") { checkIfLoggedIn().then(res => { if (res === true) { $w("#loginButton").label = "Logout"; $w("#profileButton").show(); } else { $w("#loginButton").label = "Login"; $w("#profileButton").hide(); } }) authentication.onLogin(async (member) => { const loggedInMember = await member.getMember(); memberId = loggedInMember._id; memberEmail = loggedInMember.loginEmail; return wixData.query("Members") .eq("_id", memberId) .find() .then((results) => { if (results.Members.length === 0) { const toInsert = { "_id": memberId, "email": memberEmail }; wixData.insert("Members", toInsert) .catch( (err) => { console.log(err); } ); } }) .catch( (err) => { console.log(err); }); }); } }); export async function loginButton_click(event) { if(await checkIfLoggedIn() === true) { authentication.logout(); console.log('Member is logged out.') $w("#loginButton").label = "Login"; $w("#profileButton").hide(); } else { authentication.promptLogin({"mode": "login"}) .then(() => { console.log('Member is logged in.'); $w("#loginButton").label = "Logout"; $w("#profileButton").show(); }) .catch((error) => { console.error(error); }); } } export async function profileButton_click(event) { let loggedInMember = await currentMember.getMember(); memberId = loggedInMember._id; wixLocationFrontend.to(`/items/${memberId}`); } ``` To learn more about the [wix-members-frontend module](https://www.wix.com/velo/reference/wix-members), see the API Reference.