Tutorial Video
Coming soon
About the Tutorial
Wix recently created a Wishlist tutorial using heart icons to display if item is in the User's wishlist and using 'x' icon to remove item from wishlist. Here is a link to their tutorial: https://support.wix.com/en/article/corvid-tutorial-adding-a-wishlist-to-a-wix-stores-site In our version of the Wishlist tutorial, we will use less steps to create the Wishlist and will use buttons to add or delete items from a Wishlist. As a bonus, we will also create an 'add to cart' button for single items and add the entire Wishlist to the cart in one click. Please be advised that this tutorial does NOT REQUIRE the Members Area pages but it does require for users to log into the site in order to come back and view the products they have saved.
Note: Wix Stores released a native Wishlist function that requires zero code but installing the Members Area is mandatory. Link to Wix Support article regarding their new feature: https://support.wix.com/en/article/adding-and-setting-up-a-wishlist-in-wix-stores
Follow Along #1
Tutorial Site
Logic & Requirements #1
About the Products
Our tutorial uses products with zero variations or extra choices. This allows us to use the native Wix widget that is located on the Product Details Page. Since there is no code available to detect which selections a User makes on this widget, we are unable to use it for Products that have variations. In order to create a Wishlist function for Wix Stores that have at least 1 product with variations, then you must create the Product Details page from scratch by hiding the native Wix widget and adding your own elements on the page. Or you can create each variation into it's own separate product.
Logic & Requirements #2
About the User
In order for a User to create and save a Wishlist to come back to it at a later time, the User has to be logged in as a Member of your website. You will have to make a choice and decide at what point you want the User to login. Our Wishlist tutorial has 2 methods for this.
Method #1 uses zero code by simply changing the Product Page Setting Permissions to be for Members Only. This means the User must be logged in before they can view the Product Details Page. So if the User is not logged in, the website will prompt the login window to appear. (If you created a custom login, then your custom login will be prompted.)
Method #2 uses some code on the Product Page Details to check if person is logged in before the user can see the "Add to Wishlist" button, meaning they can view the Product Page Details without being forced to be logged in first.
Method One & Two - Step #1
Create a Custom Database Collection
The Database
We will need to create one database collection to store a Members wishlist items. Using the arrow in the bottom left hand corner, open up the Site Structure panel to add a new Database. You can also add a new database using the Site Menu Database Icon.
The Settings
Make sure to click on the database settings to configure the permissions in accordance to what your website needs are. In our tutorial site, our permission settings look like this:
The Fields
In our example, we only required 1 field so we created the following for our database:
1 Reference Field to store the ID of the product from Wix Stores / #productId
The Reference Field is connected to the Stores/Products collection
Method One - Step #2
Method 1 for Members Only Permissions
(Keep scrolling to the next section if you want Public Permissions)
The Page
The page that we will be using to add the elements will be the Product Page from the Wix Stores app. Using the Site Menu icon, find the Store Pages and select Product Page. Once you open the page, you are ready to start adding the elements.
How many buttons will you add?
Depending on what design you want on your Product Page, you can either have:
One Button that does both the adding and deleting. The code will check to see if the item exists in the User's Wishlist and either add or delete the item accordingly. The button label will also change accordingly, for example: "Add to Wishlist" vs "Delete from Wishlist"
Two Buttons where one button takes care of the "Add to Wishlist" function and the second button takes care of the "Delete from Wishlist". The code will check to see if the item exists in the User's Wishlist and will either disable or enable the buttons accordingly but both buttons will remain visible at all times.
In our example, we used the following elements with the following ID names:
1 Button Element to click to add only / #addButton
1 Button Element to click to delete only / #deleteButton
1 Button Element to add or delete accordingly / #oneButton
We positioned the elements above and over the native Wix product widget on the page. Please note that sometimes by placing elements over the native widget will cause the elements to randomly 'float' away. Choose which button combination you would like on your page. Only add the button 'combination' that you prefer to use: either add the 2 buttons or the one button. Our tutorial has both so you can test and see what they would look like. The code will tell you which lines to remove depending which buttons you choose to keep or delete.
The Permissions
From the Site Menu Icon, click on Store Pages and find Product Page and click on the settings icon. Select the Permissions tab and click on Members Only. Depending on your site logic and structure, select if All Members can access the Product Page or if a specific group of paying members only.
The Code
import wixUsers from 'wix-users';
import wixLocation from 'wix-location';
import wixData from 'wix-data';
let wishlist = true;
$w.onReady(function () {
let user = wixUsers.currentUser;
let userId = user.id;
//In case the user navigations to another product using the product page navigation, we must trigger the code again
wixLocation.onChange((location) => {
let newPath = location.path; //This line gets the current URL path
$w('#productPage1').getProduct() //This line gets the current item the User is viewing
.then((product) => {
const ofProduct = product._id; //We will search the database for the item ID since we are using a reference field
wixData.query("wishlist") //We will be checking the database to see if the item already exists on their list
.hasSome("productId", ofProduct) //This line checks the column in our database labeled productId
.eq("_owner", userId) //This line checks the owner ID of the person who was logged in when they added the item to the database
.find()
.then((results) => {
if (results.items.length > 0) {
$w("#addButton").disable(); //This line is used if you are using 2 buttons
$w("#deleteButton").enable(); //This line is used if you are using 2 buttons
$w("#oneButton").label = "Delete from Wishlist"; //This line is used if you are only using 1 button
} else {
$w("#addButton").enable();
$w("#deleteButton").disable();
$w("#oneButton").label = "Add to Wishlist"; //This line is used if you are only using 1 button
}
})
.catch((err) => {
let errorMsg = err;
});
});
});
$w('#productPage1').getProduct()
.then((product) => {
const ofProduct = product._id;
wixData.query("wishlist") //We will be checking the database to see if the item already exists on their list
.hasSome("productId", ofProduct) //By using .hasSome we are able to search through a reference field (column)
.eq("_owner", userId)
.find()
.then((results) => {
if (results.items.length > 0) {
$w("#addButton").disable(); //This line is used if you are using 2 buttons
$w("#deleteButton").enable(); //This line is used if you are using 2 buttons
$w("#oneButton").label = "Delete from Wishlist"; //This line is used if you are only using 1 button
} else { //This is what will happen if there are zero matching results
$w("#addButton").enable();
$w("#deleteButton").disable();
$w("#oneButton").label = "Add to Wishlist"; //This line is used if you are only using 1 button
}
})
.catch((err) => {
let errorMsg = err;
});
});
//These lines are used if you are using 2 buttons. Your first button is to 'add' and the second button is to 'delete' from wishlist.
//This button is to 'add'
$w("#addButton").onClick((event) => {
$w('#productPage1').getProduct()
.then((product) => { //This will insert 1 record with the current item
let productName = product._id;
let toInsert = { //We will only insert the product ID, as we are using a reference field (only ID can be used)
"productId": productName
};
wixData.insert("wishlist", toInsert)
.then((results) => {
$w("#addButton").disable();
$w("#deleteButton").enable();
$w("#oneButton").label = "Delete from Wishlist"; //This line is just for this tutorial page
})
.catch((err) => {
let errorMsg = err;
});
})
});
//This button is to 'delete'
$w("#deleteButton").onClick((event) => {
$w('#productPage1').getProduct()
.then((product) => {
let ofProduct = product._id;
wixData.query("wishlist") //First we search for this matching product in the database
.hasSome("productId", ofProduct)
.eq("_owner", userId)
.find()
.then((results) => {
if (results.items.length > 0) {
let resItem = results.items[0]._id; //When the matching record is found, we get the ID of the product
console.log(resItem)
wixData.remove("wishlist", resItem) //This line will prompt the database to delete the matching record
.then((res) => {
let item = results;
$w("#addButton").enable(); //After the item is removed, we will re-enable the "add to wishlist" button
$w("#deleteButton").disable();
$w("#oneButton").label = "Add to Wishlist"; //This line is just for this tutorial page
})
.catch((err) => {
let errorMsg = err;
console.log("Cannot delete") //This error will appear in your console if record was unable to delete
});
} else {
console.log("There were no matches found") //This error will appear in your console if no matches were found
}
})
.catch((err) => {
let errorMsg = err; //This error will appear in your console if there was an error performing the query
});
});
});
//These lines are used if you are only using 1 button
$w("#oneButton").onClick((event) => {
$w('#productPage1').getProduct()
.then((product) => {
let ofProduct = product._id;
wixData.query("wishlist") //First we search for this matching product in the database
.hasSome("productId", ofProduct)
.eq("_owner", userId)
.find()
.then((results) => {
if (results.items.length > 0) { //This line counts how many matching records were found
let resItem = results.items[0]._id; //When the matching record is found, we get the ID of the product
console.log(resItem)
wixData.remove("wishlist", resItem) //Since we are using 1 button, if a match is found, we will delete it
.then((res) => {
$w("#addButton").enable(); //This line is just for this tutorial page ; delete because you are not using 2 buttons
$w("#deleteButton").disable(); //This line is just for this tutorial page ; delete because you are not using 2 buttons
$w("#oneButton").label = "Add to Wishlist"; //After deletion, we change the label of the button
})
.catch((err) => {
let errorMsg = err;
console.log("Cannot delete")
});
} else { //This line is triggered only when no matches were found
let toInsert = {
"productId": ofProduct
};
wixData.insert("wishlist", toInsert) //We insert the matching item into the database
.then(() => {
$w("#addButton").disable(); //This line is just for this tutorial page ; delete because you are not using 2 buttons
$w("#deleteButton").enable(); //This line is just for this tutorial page ; delete because you are not using 2 buttons
$w("#oneButton").label = "Delete from Wishlist"; //After inserting, we change the label of the button
})
.catch((err) => {
let errorMsg = err; //This error will appear in the console if an error occurred when trying to insert
});
}
})
.catch((err) => {
let errorMsg = err; //This error will appear in your console if there was an error performing the query
});
});
});
});
Method Two - Step #2
Method 2 for Public Permissions
The Page
The page that we will be using to add the elements will be the Product Page from the Wix Stores app. Using the Site Menu icon, find the Store Pages and select Product Page. Once you open the page, you are ready to start adding the elements.
How many buttons will you add?
Depending on what design you want on your Product Page, you can either have:
One Button that does both the adding and deleting. The code will check to see if the item exists in the User's Wishlist and either add or delete the item accordingly. The button label will also change accordingly, for example: "Add to Wishlist" vs "Delete from Wishlist"
Two Buttons where one button takes care of the "Add to Wishlist" function and the second button takes care of the "Delete from Wishlist". The code will check to see if the item exists in the User's Wishlist and will either disable or enable the buttons accordingly but both buttons will remain visible at all times.
In our example, we used the following elements with the following ID names:
1 Button Element to click to add only / #addButton
1 Button Element to click to delete only / #deleteButton
1 Button Element to add or delete accordingly / #oneButton
We positioned the elements above and over the native Wix product widget on the page. Please note that sometimes by placing elements over the native widget will cause the elements to randomly 'float' away. Choose which button combination you would like on your page. Only add the button 'combination' that you prefer to use: either add the 2 buttons or the one button. Our tutorial has both so you can test and see what they would look like. The code will tell you which lines to remove depending which buttons you choose to keep or delete.
The Permissions
From the Site Menu Icon, click on Store Pages and find Product Page and click on the settings icon. Select the Permissions tab and click on Everyone.
The Code
If you followed Method #1, you may probably notice that the code will be doubled in length simply by changing the logic of your page interaction: public viewer vs member only page. The code will tell you which lines to remove depending which buttons you choose to keep or delete.
import wixUsers from 'wix-users';
import wixLocation from 'wix-location';
import wixData from 'wix-data';
let wishlist = true;
$w.onReady(function () {
let user = wixUsers.currentUser;
let userId = user.id;
if (wixUsers.currentUser.loggedIn) {
$w('#productPage1').getProduct()
.then((product) => {
const ofProduct = product._id;
wixData.query("wishlist") //We will be checking the database to see if the item already exists on their list
.hasSome("productId", ofProduct) //By using .hasSome we are able to search through a reference field (column)
.eq("_owner", userId)
.find()
.then((results) => {
if (results.items.length > 0) {
$w("#addButton").disable(); //This line is used if you are using 2 buttons
$w("#deleteButton").enable(); //This line is used if you are using 2 buttons
$w("#oneButton").label = "Delete from Wishlist"; //This line is used if you are only using 1 button
} else { //This is what will happen if there are zero matching results
$w("#addButton").enable();
$w("#deleteButton").disable();
$w("#oneButton").label = "Add to Wishlist"; //This line is used if you are only using 1 button
}
})
.catch((err) => {
let errorMsg = err;
});
});
} else {
$w("#addButton").label = "Login to Save"; //Delete this line if you are using the one button method
$w("#oneButton").label = "Login to Save"; //Delete this line if you are using the two button method
}
//In case the user navigations to another product using the product page navigation, we must trigger the code again
wixLocation.onChange((location) => {
if (wixUsers.currentUser.loggedIn) {
let newPath = location.path; //This line gets the current URL path
$w('#productPage1').getProduct() //This line gets the current item the User is viewing
.then((product) => {
const ofProduct = product._id; //We will search the database for the item ID since we are using a reference field
wixData.query("wishlist") //We will be checking the database to see if the item already exists on their list
.hasSome("productId", ofProduct) //This line checks the column in our database labeled productId
.eq("_owner", userId) //This line checks the owner ID of the person who was logged in when they added the item to the database
.find()
.then((results) => {
if (results.items.length > 0) {
$w("#addButton").disable(); //This line is used if you are using 2 buttons
$w("#deleteButton").enable(); //This line is used if you are using 2 buttons
$w("#oneButton").label = "Delete from Wishlist"; //This line is used if you are only using 1 button
} else {
$w("#addButton").enable();
$w("#deleteButton").disable();
$w("#oneButton").label = "Add to Wishlist"; //This line is used if you are only using 1 button
}
})
.catch((err) => {
let errorMsg = err;
});
});
} else {
$w("#addButton").label = "Login to Save"; //Delete this line if you are using the one button method
$w("#oneButton").label = "Login to Save"; //Delete this line if you are using the two button method
}
});
//These lines are used if you are using 2 buttons. Your first button is to 'add' and the second button is to 'delete' from wishlist.
//This button is to 'add'
$w("#addButton").onClick((event) => {
if (wixUsers.currentUser.loggedIn) {
$w('#productPage1').getProduct()
.then((product) => { //This will insert 1 record with the current item
let productName = product._id;
let toInsert = { //We will only insert the product ID, as we are using a reference field (only ID can be used)
"productId": productName
};
wixData.insert("wishlist", toInsert)
.then((results) => {
$w("#addButton").disable();
$w("#deleteButton").enable();
$w("#oneButton").label = "Delete from Wishlist"; //This line is just for this tutorial page
})
.catch((err) => {
let errorMsg = err;
});
})
} else {
wixUsers.promptLogin()
.then((theUser) => {
let theId = user.id;
let isLoggedIn = user.loggedIn;
$w('#productPage1').getProduct()
.then((product) => { //This will insert 1 record with the current item
let productName = product._id;
let toInsert = { //We will only insert the product ID, as we are using a reference field (only ID can be used)
"productId": productName
};
wixData.insert("wishlist", toInsert)
.then((results) => {
$w("#addButton").disable();
$w("#deleteButton").enable();
$w("#oneButton").label = "Delete from Wishlist"; //This line is just for this tutorial page
})
.catch((err) => {
let errorMsg = err;
});
})
})
.catch((err) => {
let errorMsg = err; // "The user closed the login dialog"
});
}
});
//This button is to 'delete'
$w("#deleteButton").onClick((event) => {
$w('#productPage1').getProduct()
.then((product) => {
let ofProduct = product._id;
wixData.query("wishlist") //First we search for this matching product in the database
.hasSome("productId", ofProduct)
.eq("_owner", userId)
.find()
.then((results) => {
if (results.items.length > 0) {
let resItem = results.items[0]._id; //When the matching record is found, we get the ID of the product
console.log(resItem)
wixData.remove("wishlist", resItem) //This line will prompt the database to delete the matching record
.then((res) => {
let item = results;
$w("#addButton").enable(); //After the item is removed, we will re-enable the "add to wishlist" button
$w("#deleteButton").disable();
$w("#oneButton").label = "Add to Wishlist"; //This line is just for this tutorial page
})
.catch((err) => {
let errorMsg = err;
console.log("Cannot delete") //This error will appear in your console if record was unable to delete
});
} else {
console.log("There were no matches found") //This error will appear in your console if no matches were found
}
})
.catch((err) => {
let errorMsg = err; //This error will appear in your console if there was an error performing the query
});
});
});
//These lines are used if you are only using 1 button
$w("#oneButton").onClick((event) => {
if (wixUsers.currentUser.loggedIn) {
$w('#productPage1').getProduct()
.then((product) => {
let ofProduct = product._id;
wixData.query("wishlist") //First we search for this matching product in the database
.hasSome("productId", ofProduct)
.eq("_owner", userId)
.find()
.then((results) => {
if (results.items.length > 0) { //This line counts how many matching records were found
let resItem = results.items[0]._id; //When the matching record is found, we get the ID of the product
console.log(resItem)
wixData.remove("wishlist", resItem) //Since we are using 1 button, if a match is found, we will delete it
.then((res) => {
$w("#addButton").enable(); //This line is just for this tutorial page ; delete because you are not using 2 buttons
$w("#deleteButton").disable(); //This line is just for this tutorial page ; delete because you are not using 2 buttons
$w("#oneButton").label = "Add to Wishlist"; //After deletion, we change the label of the button
})
.catch((err) => {
let errorMsg = err;
console.log("Cannot delete")
});
} else { //This line is triggered only when no matches were found
let toInsert = {
"productId": ofProduct
};
wixData.insert("wishlist", toInsert) //We insert the matching item into the database
.then(() => {
$w("#addButton").disable(); //This line is just for this tutorial page ; delete because you are not using 2 buttons
$w("#deleteButton").enable(); //This line is just for this tutorial page ; delete because you are not using 2 buttons
$w("#oneButton").label = "Delete from Wishlist"; //After inserting, we change the label of the button
})
.catch((err) => {
let errorMsg = err; //This error will appear in the console if an error occurred when trying to insert
});
}
})
.catch((err) => {
let errorMsg = err; //This error will appear in your console if there was an error performing the query
});
});
} else {
wixUsers.promptLogin()
.then((theUser) => {
let theId = user.id;
let isLoggedIn = user.loggedIn;
$w('#productPage1').getProduct()
.then((product) => { //This will insert 1 record with the current item
let productName = product._id;
let toInsert = { //We will only insert the product ID, as we are using a reference field (only ID can be used)
"productId": productName
};
wixData.insert("wishlist", toInsert)
.then((results) => {
$w("#addButton").disable();
$w("#deleteButton").enable();
$w("#oneButton").label = "Delete from Wishlist"; //This line is just for this tutorial page
})
.catch((err) => {
let errorMsg = err;
});
})
})
.catch((err) => {
let errorMsg = err; // "The user closed the login dialog"
});
}
});
});
Let's Recap
Regardless of which Method you chose, so far this is what you have completed:
One Database Collection has been created to store wishlist items for website Members
One or Two Button Elements have been added on your Wix Stores Product Page
Product Page Permissions have been configured on your Wix Stores Product Page
Page Code has been added on your Wix Stores Product Page to trigger the correct function for each button.
Method One & Two - Step #3
Create a Regular Page to display Wishlist
The Page
The page that we will be using to add the elements for this tutorial will be a regular page set to Members Only permissions.
The Elements
In our example, we used the following elements with the following ID names:
1 Button Element to click to add all items to card / #bulkAdd2Cart
1 Button Element to load more items / #loadMoreButton
1 Text Element to click to display success message / #successText
1 Repeater Element to display wishlist items / #oneButton
1 Repeater Button Element to add single item to cart / #add2CartButton
1 Repeater Button Element to delete single item from wishlist / #deleteItemButton
1 Collapsed Strip Element to display message when wishlist is empty / #noWishList
1 Transparent Strip Element to display load more button / #loadMoreStrip
1 Dataset Element to connect to wishlist database collection / #wishlistDataset
We added other regular text and button elements for simple titles and call to actions (that were not connected via code). The dataset is connected to our wishlist database collection with the following settings:
Connections via Dataset
With the exception of the'Add to Cart' button, all of the other repeater elements were connected directly to the #wishListDataset. We connected our image and product name text element through the dataset via the reference field. This reference field allows us to view the available fields that are located in the Stores/Products database collection. We connected our delete button to the delete function on our dataset to allow the Member to delete that single item from their wishlist. Our settings looked something like this:
The Code - Add to Cart
To allow a Member to add an individual repeater referenced item to the Wix Stores Cart we connected the 'Add to Cart' button via code. In order for this to work, you must have a shopping cart element on your page. For our example, the Wix Stores App automatically added a shopping cart element pinned to our header area.
This is what our code looks like:
$w("#repeater1").onItemReady(($item, itemData, index) => {
$item("#add2CartButton").onClick((event) => {
let id = itemData.productId._id; //The word productId is replaced with the field key from your database collection that is used as the reference for the products
$w("#shoppingCartIcon1").addToCart(id) //This is the ID of your shopping cart element
.then(() => {
console.log("Product added");
})
.catch((error) => {
console.log(error);
});
});
});
Because we don't know if a Member will delete one, multiple or all items from their wishlist we decided to add a code to check the number of items there are when the dataset changes. If there are zero number of items on the Members wishlist, we want to expand a collapsed strip to encourage a call to action. In our example, we added a regular button and linked it to the shop page. Our code looks like this:
$w("#wishlistDataset").onCurrentIndexChanged((index) => {
let count = $w("#wishlistDataset").getTotalCount();
if (count > 0) {
$w('#wishlistStrip').expand(); //This is the strip that has your wishlist repeater
$w("#loadMoreStrip").expand(); //If you do not have a second strip for your load more button, then delete this line.
$w("#noWishlist").collapse();
} else {
$w('#wishlistStrip').collapse();
$w("#loadMoreStrip").collapse(); //If you do not have a second strip for your load more button, then delete this line.
$w("#noWishlist").expand();
}
});
Since we want to trigger this same code when the page first loads, we will add all the code to our onReady section. Altogether, our code looks like this:
import wixCrm from 'wix-crm-backend';
import wixData from 'wix-data';
$w.onReady(function () {
$w("#repeater1").onItemReady(($item, itemData, index) => {
$item("#add2CartButton").onClick((event) => {
let id = itemData.productId._id; //The word productId is replaced with the field key from your database collection that is used as the reference for the products
$w("#shoppingCartIcon1").addToCart(id)
.then(() => {
console.log("Product added");
})
.catch((error) => {
console.log(error);
});
});
});
$w("#wishlistDataset").onCurrentIndexChanged((index) => {
let count = $w("#wishlistDataset").getTotalCount();
if (count > 0) {
$w('#wishlistStrip').expand();
$w("#loadMoreStrip").expand(); //If you do not have a second strip for your load more button, then delete this line.
$w("#noWishlist").collapse();
} else {
$w('#wishlistStrip').collapse();
$w("#loadMoreStrip").collapse(); //If you do not have a second strip for your load more button, then delete this line.
$w("#noWishlist").expand();
}
});
$w("#wishlistDataset").onReady(() => {
console.log("The dataset is ready");
let count = $w("#wishlistDataset").getTotalCount();
if (count > 0) {
$w('#wishlistStrip').expand();
$w("#loadMoreStrip").expand(); //If you do not have a second strip for your load more button, then delete this line.
$w("#noWishlist").collapse();
} else {
$w('#wishlistStrip').collapse();
$w("#loadMoreStrip").collapse(); //If you do not have a second strip for your load more button, then delete this line.
$w("#noWishlist").expand();
}
});
});
Method One & Method Two - Bonus Step
Create 'Bulk Add to Cart' Function
We continue to add additional code after the onReady section. We decided to make it a bit easier to add bulk items to the Wix Stores Cart. We triggered our #bulkAdd2Cart button via code to get all items from the current Member's wishlist and insert all of the items into the Wix Stores Cart. Upon successfully adding all items, we wanted to display a success message for a short period of time to give a visual confirmation to the Member.
Our code looks like this:
const selectedItems = [];
export async function bulkAdd2Cart_click(event, $w) {
let itemsToAdd = [];
$w("#repeater1").forEachItem(($item, itemData, index) => { //This line will loop around for each item on the wishlist to repeat the steps
let prod = itemData.productId._id; //This line gets the ID of that item
itemsToAdd.push({ productID: prod, quantity: 1, options: {} }); //This line pushed all items into an array with the ID number, the quantiy number and the product options
});
let res = await $w('#shoppingCartIcon1').addProductsToCart(itemsToAdd).then(() => {
console.log("Product added");
$w("#successText").show(); //This is our success message
setTimeout(() => {
$w("#successText").hide(); //This is our success message
}, 5000); //This is the time in milliseconds
})
.catch((error) => {
console.log(error);
});
}
Author
by Code Queen
Stuck on a project? Hire Code Queen, LLC!
Schedule a phone call or video call directly online. In a different time zone? No problem! Code Queen currently has clients around the world.
Online Booking: Discovery Session
Contact Form: Send project details