E-commerce websites have become increasingly popular due to their convenience and accessibility. They allow users to buy goods over the internet and have them delivered. In this tutorial, you will learn how to build an e-commerce website using SvelteKit and Strapi CMS.
To follow along with this tutorial, ensure you meet the following criteria:
To get started, let’s create a new Strapi project that will serve as the application backend. To do that, open your terminal, navigate to the directory where you want to install the project and run the command below.
npx create-strapi-app@latest my-project --quickstart
After the project installation is completed, the application development server will start automatically. When you open the application in your browser, you will be required to create a new account.
After creating a new Strapi application account, you will be redirected to the application dashboard, as shown in the screenshot below.
Now, let’s create the Collection Types that define how the application's data is structured and managed. We will create the Product
and cartItem
collection types by following the steps below.
Product
Collection Typeproduct
as the display name, and click on the "continue" button as shown in the screenshot below.Next, let’s structure how data will be managed in the product
collection type by adding the following fields:
productName
: type→textproductDescription
: type→textproductImage
: type→ multiple mediaproductPrice
: type→numberAfter adding the fields above, click the "Save" button, as shown in the screenshot below.
Repeat the above steps to create the collection type below.
Create a collection type named cartItem
and add the following fields:
userID
: type→numberproductID
: type→textWe need to enable public access for each to interact with the created collection types via an API call. To do this, navigate to Settings → Roles → Public from your Strapi side menu and select all operations for each collection type.
Now, let’s add items to the Product collection type. To do this, navigate to the Content Manager from the side menu and click on the Product collection type. Then, add as many products as you like, as shown in the screenshot below.
In this project, we'll use the Strapi GraphQL plugin for user authentication. To install it, open a terminal in your project folder and run the following command.
npm install @strapi/plugin-graphql
Now, let’s start creating the application frontend using SvelteKit. To scaffold a new SvelteKit project, run the command below.
npx degit sveltejs/template my-ecommerce-app
cd my-ecommerce-app
npm install
In this tutorial, we will use the Svelte-routing library to handle the application's page navigation. To install the library, run the command below:
npm install svelte-routing
After the installation, open the project in your preferred code editor or IDE.
Now, let’s start creating the application functionalities and UI, such as the user authentication page, home page, and cart page. To create the application, follow the steps below.
The application's home page will display a list of all available products in the store for users to browse. To create this home page, navigate to the src
folder in your project's root directory and create a new folder named Components
to store the application’s component files. Inside the Components
folder, create a file named Home.svelte
and add the following code:
1<!-- Home.svelte -->
2<script>
3import { onMount } from "svelte";
4import { Link } from "svelte-routing";
5import Navbar from "./Navbar.svelte";
6
7let products = [];
8
9onMount(async () => {
10 try {
11 const response = await fetch(
12 "http://localhost:1337/api/products?populate=*",
13 );
14 const data = await response.json();
15 products = data.data.map((item) => {
16 const productImage = item.productImage[0]; // Access the first image
17 const imageUrl = productImage.formats?.large?.url || productImage.url; // Use large format if available
18 return {
19 id: item.documentId,
20 name: item.productName,
21 description: item.productDescription,
22 price: parseFloat(item.productPrice),
23 imageUrl: imageUrl,
24 };
25 });
26 } catch (error) {
27 console.error("Error fetching products:", error);
28 }
29});
30</script>
31
32<Navbar />
33
34<main>
35 <h1>Main Store</h1>
36 <div class="products">
37 {#each products as product}
38 <div class="product-card">
39 <img
40 src={`http://localhost:1337${product.imageUrl}`}
41 alt={product.name}
42 />
43 <div class="details">
44 <h2>{product.name}</h2>
45 <p>{product.description}</p>
46 <p class="price">${product.price.toFixed(2)}</p>
47 <Link to={`/product/${product.id}`}>
48 <button>View Product</button>
49 </Link>
50 </div>
51 </div>
52 {/each}
53 </div>
54 </main>
55
56<style>
57html,
58body {
59 margin: 0;
60 padding: 0;
61 width: 100%;
62 height: 100%;
63 overflow-x: hidden;
64}
65
66body {
67 font-family: Arial, sans-serif;
68 display: flex;
69 flex-direction: column;
70}
71
72nav {
73 background-color: #71b3bf;
74 color: white;
75 padding: 1em 0;
76 width: 100%;
77 box-sizing: border-box;
78}
79
80.nav-container {
81 display: flex;
82 justify-content: space-between;
83 align-items: center;
84 padding: 0 2em;
85 max-width: 1200px;
86 margin: 0 auto;
87}
88
89.nav-brand a {
90 color: white;
91 text-decoration: none;
92 font-size: 1.5em;
93 font-weight: bold;
94}
95
96.nav-links {
97 display: flex;
98 gap: 1em;
99}
100
101.nav-links a {
102 color: white;
103 text-decoration: none;
104 font-size: 1em;
105}
106
107.nav-links a:hover {
108 text-decoration: underline;
109}
110
111main {
112 padding: 2em;
113 width: 100%;
114 box-sizing: border-box;
115 flex: 1;
116}
117
118h1 {
119 color: #ff3e00;
120 text-transform: uppercase;
121 font-size: 2.5em;
122 font-weight: 300;
123 margin-bottom: 1em;
124}
125
126.products {
127 display: flex;
128 flex-wrap: wrap;
129 gap: 2em;
130 justify-content: center;
131}
132
133.product-card {
134 background: #fff;
135 border: 1px solid #ddd;
136 border-radius: 10px;
137 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
138 overflow: hidden;
139 flex: 1 1 calc(33.333% - 2em);
140 max-width: calc(33.333% - 2em);
141 transition: transform 0.2s;
142 box-sizing: border-box;
143}
144
145.product-card:hover {
146 transform: scale(1.05);
147}
148
149.product-card img {
150 width: 100%;
151 height: 200px;
152 object-fit: cover;
153}
154
155.details {
156 padding: 1em;
157 text-align: left;
158}
159
160.details h2 {
161 margin: 0 0 0.5em;
162 font-size: 1.5em;
163 color: #333;
164}
165
166.details p {
167 margin: 0;
168 color: #666;
169}
170
171.price {
172 margin-top: 0.5em;
173 font-size: 1.2em;
174 font-weight: bold;
175 color: #ff3e00;
176 padding-top: 10px;
177}
178button {
179 background-color: transparent;
180 color: #71b3bf;
181 border: 1px solid #71b3bf;
182 padding: 0.75em 1.5em;
183 border-radius: 5px;
184 cursor: pointer;
185 font-size: 1em;
186 transition: background-color 0.3s;
187 margin-left: 30%;
188 margin-top: 25px;
189}
190
191button:hover {
192 background-color: #71b3bf;
193 color: white;
194 border: none;
195}
196
197@media (max-width: 768px) {
198 .product-card {
199 flex: 1 1 calc(50% - 2em);
200 max-width: calc(50% - 2em);
201 }
202}
203
204@media (max-width: 480px) {
205 .product-card {
206 flex: 1 1 100%;
207 max-width: 100%;
208 }
209}
210</style>
In the above code, the onMount
method fetches all the available products from the backend by making a GET
request to http://localhost:1337/api/products?populate=*
. The products are then rendered in the user interface inside the main component.
Here is what the home page looks like:
To create the registration page, in the src/Components
directory, create a file named Register.svelte
and add the following code to it:
1<!-- Register.svelte -->
2<script>
3import { createEventDispatcher } from "svelte";
4import { navigate } from "svelte-routing";
5let email = "";
6let username = "";
7let password = "";
8const dispatch = createEventDispatcher();
9
10const handleRegister = async () => {
11 try {
12 const response = await fetch("http://localhost:1337/graphql", {
13 method: "POST",
14 headers: {
15 "Content-Type": "application/json",
16 },
17 body: JSON.stringify({
18 query: `
19 mutation {
20 register(input: {
21 username: "${username}",
22 email: "${email}",
23 password: "${password}"
24 }) {
25 jwt
26 user {
27 id
28 username
29 email
30 }
31 }
32 }
33 `,
34 }),
35 });
36
37 const responseData = await response.json();
38
39 if (responseData.errors) {
40 let errorMsg = "Registration failed";
41 if (responseData.errors[0]?.message) {
42 errorMsg = responseData.errors[0].message;
43 }
44 throw new Error(errorMsg);
45 }
46
47 alert(`Registered successfully.`);
48 navigate(`/login`);
49 } catch (error) {
50 alert(`Error: ${error.message}`);
51 }
52};
53
54const handleLoginLinkClick = () => {
55 navigate(`/login`);
56};
57</script>
58
59<main>
60 <h1>Register</h1>
61 <form on:submit|preventDefault={handleRegister}>
62 <label>
63 Username:
64 <input type="text" bind:value={username} required>
65 </label>
66 <label>
67 Email:
68 <input type="email" bind:value={email} required>
69 </label>
70 <label>
71 Password:
72 <input type="password" bind:value={password} required>
73 </label>
74 <button type="submit">Register</button>
75 </form>
76 <p>Already have an account? <a href="login" on:click|preventDefault={handleLoginLinkClick}>Login</a></p>
77</main>
78
79<style>
80main {
81 text-align: center;
82 padding: 1em;
83 max-width: 240px;
84 margin: 0 auto;
85}
86
87h1 {
88 color: #ff3e00;
89 text-transform: uppercase;
90 font-size: 2em;
91 font-weight: 100;
92}
93
94label {
95 display: block;
96 margin: 0.5em 0;
97}
98
99input {
100 width: 100%;
101 padding: 0.5em;
102 margin: 0.5em 0;
103}
104
105button {
106 padding: 0.5em 1em;
107 background-color: #ff3e00;
108 color: white;
109 border: none;
110 cursor: pointer;
111}
112
113button:hover {
114 background-color: #e63600;
115}
116
117p {
118 margin-top: 1em;
119}
120
121a {
122 color: #ff3e00;
123 text-decoration: none;
124}
125
126a:hover {
127 text-decoration: underline;
128}
129</style>
From the code above, we create a registration form that accepts the user's information as input and stores it in the Strapi backend via an API call. Once the registration is successful, the user is redirected to the login page.
Below is what the registration page looks like:
To create the login page, create a file named Login.svelte
inside the Components
folder and add the following code:
1<!-- Login.svelte -->
2<script>
3import { createEventDispatcher } from "svelte";
4import { navigate } from "svelte-routing";
5
6let email = "";
7let password = "";
8const dispatch = createEventDispatcher();
9
10const handleLogin = async () => {
11 try {
12 const response = await fetch("http://localhost:1337/graphql", {
13 method: "POST",
14 headers: {
15 "Content-Type": "application/json",
16 },
17 body: JSON.stringify({
18 query: `
19 mutation {
20 login(input: { identifier: "${email}", password: "${password}" }) {
21 jwt
22 user {
23 id
24 username
25 email
26 }
27 }
28 }
29 `,
30 }),
31 });
32
33 const responseData = await response.json();
34
35 if (responseData.errors) {
36 let errorMsg = "Login failed";
37 if (responseData.errors[0]?.message) {
38 errorMsg = responseData.errors[0].message;
39 }
40 throw new Error(errorMsg);
41 }
42
43 const user = responseData.data.login.user;
44 alert(`Logged in successfully`);
45 navigate(`/`);
46
47 // Set cookies for user data
48 document.cookie = `email=${user.email}; path=/; max-age=${7 * 24 * 60 * 60}`;
49 document.cookie = `username=${user.username}; path=/; max-age=${7 * 24 * 60 * 60}`;
50 document.cookie = `userId=${user.id}; path=/; max-age=${7 * 24 * 60 * 60}`;
51 } catch (error) {
52 alert(`Error: ${error.message}`);
53 }
54};
55
56const handleRegisterLinkClick = () => {
57 navigate(`/register`);
58};
59</script>
60
61<main>
62 <h1>Login</h1>
63 <form on:submit|preventDefault={handleLogin}>
64 <label>
65 Email:
66 <input type="email" bind:value={email} required>
67 </label>
68 <label>
69 Password:
70 <input type="password" bind:value={password} required>
71 </label>
72 <button type="submit">Login</button>
73 </form>
74 <p>Don't have an account? <a href="#" on:click={handleRegisterLinkClick}>Register</a></p>
75 </main>
76
77<style>
78main {
79 text-align: center;
80 padding: 1em;
81 max-width: 240px;
82 margin: 0 auto;
83}
84
85h1 {
86 color: #ff3e00;
87 text-transform: uppercase;
88 font-size: 2em;
89 font-weight: 100;
90}
91
92label {
93 display: block;
94 margin: 0.5em 0;
95}
96
97input {
98 width: 100%;
99 padding: 0.5em;
100 margin: 0.5em 0;
101}
102
103button {
104 padding: 0.5em 1em;
105 background-color: #ff3e00;
106 color: white;
107 border: none;
108 cursor: pointer;
109}
110
111button:hover {
112 background-color: #e63600;
113}
114
115p {
116 margin-top: 1em;
117}
118
119a {
120 color: #ff3e00;
121 text-decoration: none;
122}
123
124a:hover {
125 text-decoration: underline;
126}
127</style>
In the code above, we create a login form that accepts the user's email and password as input. If the login details are correct, the user's details are stored in cookies; otherwise, a login error is displayed for the user.
Here is what the login page looks like:
Now, let’s create the product preview page showing more details about each product. To do this, create a file named Product.svelte
inside the Components
folder and add the following code:
1<script>
2import { onMount } from "svelte";
3import { Link, navigate } from "svelte-routing";
4import Navbar from "./Navbar.svelte";
5export let id;
6
7let product = null;
8let userId = getCookie("userId");
9let successMessage = "";
10let errorMessage = "";
11
12function getCookie(name) {
13 const value = `; ${document.cookie}`;
14 const parts = value.split(`; ${name}=`);
15 if (parts.length === 2) return parts.pop().split(";").shift();
16}
17
18onMount(async () => {
19 if (!userId) {
20 navigate("/login");
21 } else {
22 try {
23 const response = await fetch(
24 `http://localhost:1337/api/products/${id}?populate=*`,
25 );
26 if (!response.ok) throw new Error("Failed to fetch product");
27
28 const data = await response.json();
29 const attributes = data.data;
30 const productImage =
31 attributes.productImage[0]?.formats?.large?.url ||
32 attributes.productImage[0]?.url;
33
34 product = {
35 id: attributes.id,
36 name: attributes.productName,
37 description: attributes.productDescription,
38 price: parseFloat(attributes.productPrice),
39 imageUrl: productImage ? `http://localhost:1337${productImage}` : null,
40 };
41 } catch (error) {
42 errorMessage = "Error fetching product details.";
43 console.error("Error fetching product:", error);
44 }
45 }
46});
47
48const addToCart = async () => {
49 try {
50 const response = await fetch(`http://localhost:1337/api/cart-items`, {
51 method: "POST",
52 headers: {
53 "Content-Type": "application/json",
54 },
55 body: JSON.stringify({
56 data: {
57 userID: userId,
58 productID: id,
59 },
60 }),
61 });
62
63 if (!response.ok) {
64 throw new Error("Error adding product to cart.");
65 }
66
67 successMessage = "Product added to cart successfully!";
68 setTimeout(() => (successMessage = ""), 3000); // Clear message after 3 seconds
69 } catch (error) {
70 console.error("Error adding product to cart:", error);
71 errorMessage = "Failed to add product to cart.";
72 }
73};
74</script>
75
76<Navbar />
77<main>
78 {#if errorMessage}
79 <p class="error-message">{errorMessage}</p>
80 {/if}
81
82 {#if product}
83 <div class="product-container">
84 {#if product.imageUrl}
85 <img src={product.imageUrl} alt={product.name} />
86 {/if}
87 <div class="product-details">
88 <h1>{product.name}</h1>
89 <p>{product.description}</p>
90 <p class="price">${product.price.toFixed(2)}</p>
91 <button on:click={addToCart}>Add to Cart{userId}</button>
92 {#if successMessage}
93 <p class="success-message">{successMessage}</p>
94 <p><Link to="/cart/">View Cart</Link></p>
95 {/if}
96 <p><Link to="/">Back to Home</Link></p>
97 </div>
98 </div>
99 {:else}
100 <p>Product not found</p>
101 {/if}
102</main>
103
104<style>
105html,
106body {
107 margin: 0;
108 padding: 0;
109 width: 100%;
110 height: 100%;
111 overflow-x: hidden;
112 font-family: Arial, sans-serif;
113}
114
115body {
116 display: flex;
117 flex-direction: column;
118 align-items: center;
119 justify-content: center;
120}
121
122main {
123 display: flex;
124 justify-content: center;
125 align-items: center;
126 width: 100%;
127 height: 100%;
128 box-sizing: border-box;
129 padding: 2em;
130 background-color: #f9f9f9;
131}
132
133.product-container {
134 display: flex;
135 flex-direction: column;
136 align-items: center;
137 background: #fff;
138 border: 1px solid #ddd;
139 border-radius: 10px;
140 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
141 overflow: hidden;
142 max-width: 600px;
143 width: 100%;
144 box-sizing: border-box;
145 padding: 2em;
146}
147
148.product-container img {
149 width: 50%;
150 height: auto;
151 border-bottom: 1px solid #ddd;
152 margin-bottom: 1em;
153}
154
155.product-details {
156 text-align: center;
157}
158
159h1 {
160 color: #ff3e00;
161 text-transform: uppercase;
162 font-size: 2em;
163 font-weight: 300;
164 margin: 0 0 1em;
165}
166
167p {
168 font-size: 1em;
169 color: #666;
170 margin: 0 0 1em;
171}
172
173.price {
174 font-size: 1.5em;
175 font-weight: bold;
176 color: #ff3e00;
177 margin: 0.5em 0;
178}
179
180button {
181 background-color: #ff3e00;
182 color: white;
183 border: none;
184 padding: 0.75em 1.5em;
185 border-radius: 5px;
186 cursor: pointer;
187 font-size: 1em;
188 transition: background-color 0.3s;
189}
190
191button:hover {
192 background-color: #e03500;
193}
194
195.success-message {
196 color: green;
197 font-weight: bold;
198 margin-top: 1em;
199}
200
201a {
202 color: #ff3e00;
203 text-decoration: none;
204 font-size: 1em;
205}
206
207a:hover {
208 text-decoration: underline;
209}
210</style>
From the code above, we fetch the details of a product from the Strapi endpoint using the product ID and render the product details, allowing the user to add the product to the cart.
From the image below, we can see the details of a product.
To create the cart page, create a file named Cart.svelte
and add the following code to it:
1<script>
2import { onMount } from "svelte";
3import { navigate } from "svelte-routing";
4import { Link } from "svelte-routing";
5import Navbar from "./Navbar.svelte";
6
7// Helper function to get cookie values
8function getCookie(name) {
9 const value = `; ${document.cookie}`;
10 const parts = value.split(`; ${name}=`);
11 if (parts.length === 2) return parts.pop().split(";").shift();
12}
13
14let userId = getCookie("userId");
15let cart = [];
16let message = "";
17
18// Fetch cart items for the logged-in user
19const fetchCartItems = async () => {
20 try {
21 const cartResponse = await fetch(`http://localhost:1337/api/cart-items`);
22 const cartData = await cartResponse.json();
23 // Filter cart items based on the user's ID
24 const userCartItems = cartData.data.filter((item) => item.userID == userId);
25
26 // Fetch product details for each cart item
27 const products = await Promise.all(
28 userCartItems.map(async (item) => {
29 const productResponse = await fetch(
30 `http://localhost:1337/api/products/${item.productID}?populate=*`,
31 );
32 const productData = await productResponse.json();
33 const attributes = productData.data;
34
35 // Fetch appropriate image URL (large, medium, or fallback to default URL)
36 const imageUrl =
37 attributes.productImage[0]?.formats?.large?.url ||
38 attributes.productImage[0]?.url;
39
40 return {
41 id: attributes.documentId, // Using documentId from the product data
42 item_id: item.documentId, // Item documentId from the cart item
43 name: attributes.productName,
44 description: attributes.productDescription,
45 price: parseFloat(attributes.productPrice),
46 imageUrl: `http://localhost:1337${imageUrl}`, // Construct full image URL
47 };
48 }),
49 );
50 cart = products;
51 } catch (error) {
52 console.error("Error fetching cart items:", error);
53 }
54};
55
56// Function to delete an item from the cart
57const deleteItem = async (cartItemId) => {
58 try {
59 const response = await fetch(
60 `http://localhost:1337/api/cart-items/${cartItemId}`,
61 {
62 method: "DELETE",
63 },
64 );
65 if (response.ok) {
66 console.log(`Item ${cartItemId} deleted successfully.`);
67 cart = cart.filter((product) => product.item_id !== cartItemId); // Update cart locally
68 message = "Item removed successfully!";
69 setTimeout(() => (message = ""), 3000);
70 } else {
71 console.error(`Failed to delete item ${cartItemId}.`);
72 message = "Failed to remove item.";
73 setTimeout(() => (message = ""), 3000);
74 }
75 } catch (error) {
76 console.error("Error deleting item:", error);
77 message = "Error removing item.";
78 setTimeout(() => (message = ""), 3000);
79 }
80};
81
82// onMount function to fetch cart items when the component is mounted
83onMount(() => {
84 if (!userId) {
85 navigate("/login"); // Redirect to login if userId is not found
86 } else {
87 fetchCartItems();
88 }
89});
90
91// Handle purchase logic
92const handlePurchase = () => {
93 alert("You will be redirected to a payment gateway");
94 navigate("/");
95};
96
97// Calculate total price of items in the cart
98$: totalPrice = cart
99 .reduce((total, product) => total + product.price, 0)
100 .toFixed(2);
101</script>
102
103<Navbar />
104<main>
105 {#if cart.length > 0}
106 <div class="checkout-container">
107 <h1>Cart</h1>
108 <ul class="product-list">
109 {#if message}
110 <div class="message">{message}</div>
111 {/if}
112 {#each cart as product}
113 <li class="product-item">
114 <img src={product.imageUrl} alt={product.name} />
115 <div class="product-details">
116 <p><strong>{product.name}</strong></p>
117 <p>{product.description}</p>
118 <p>Price: ${product.price.toFixed(2)}</p>
119 </div>
120 <button
121 class="delete-button"
122 on:click={() => deleteItem(product.item_id)}>Remove</button
123 >
124 </li>
125 {/each}
126 </ul>
127 <div class="total-price">
128 <p><strong>Total Price: ${totalPrice}</strong></p>
129 </div>
130 <button on:click={handlePurchase}>Check Out</button>
131 <p><Link to="/">Back to Home</Link></p>
132 </div>
133 {:else}
134 <p>Your cart is empty</p>
135 {/if}
136</main>
137
138<style>
139html,
140body {
141 margin: 0;
142 padding: 0;
143 width: 100%;
144 height: 100%;
145 overflow-x: hidden;
146 font-family: Arial, sans-serif;
147}
148
149body {
150 display: flex;
151 flex-direction: column;
152 align-items: center;
153 justify-content: center;
154}
155
156main {
157 display: flex;
158 justify-content: center;
159 align-items: center;
160 width: 100%;
161 height: 100%;
162 box-sizing: border-box;
163 padding: 2em;
164 background-color: #f9f9f9;
165}
166
167.checkout-container {
168 display: flex;
169 flex-direction: column;
170 align-items: center;
171 background: #fff;
172 border: 1px solid #ddd;
173 border-radius: 10px;
174 box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
175 padding: 2em;
176 max-width: 600px;
177 width: 100%;
178 box-sizing: border-box;
179}
180
181.checkout-container h1 {
182 color: #ff3e00;
183 text-transform: uppercase;
184 font-size: 2em;
185 font-weight: 300;
186 margin: 0 0 1em;
187}
188
189.product-list {
190 list-style: none;
191 padding: 0;
192 margin: 0 0 2em;
193 width: 100%;
194}
195
196.product-item {
197 display: flex;
198 align-items: center;
199 border-bottom: 1px solid #ddd;
200 padding: 1em 0;
201}
202
203.product-item img {
204 width: 150px;
205 height: auto;
206 margin-right: 1em;
207 border-radius: 5px;
208}
209
210.product-details {
211 flex-grow: 1;
212}
213
214.product-details p {
215 margin: 0.5em 0;
216 font-size: 1em;
217 color: #666;
218}
219
220.total-price {
221 font-size: 1.2em;
222 font-weight: bold;
223 margin-bottom: 1em;
224}
225
226button {
227 background-color: #ff3e00;
228 color: white;
229 border: none;
230 padding: 0.75em 1.5em;
231 border-radius: 5px;
232 cursor: pointer;
233 font-size: 1em;
234 transition: background-color 0.3s;
235 margin-bottom: 1em;
236}
237
238button:hover {
239 background-color: #e03500;
240}
241
242a {
243 color: #ff3e00;
244 text-decoration: none;
245 font-size: 1em;
246}
247
248a:hover {
249 text-decoration: underline;
250}
251
252.delete-button {
253 background-color: #e03500;
254 color: white;
255 border: none;
256 padding: 0.5em 1em;
257 border-radius: 5px;
258 cursor: pointer;
259 font-size: 0.9em;
260 transition: background-color 0.3s;
261 margin-left: 1em;
262}
263
264.delete-button:hover {
265 background-color: #d02e00;
266}
267
268.message {
269 background-color: #dff0d8;
270 color: #3c763d;
271 padding: 1em;
272 border-radius: 5px;
273 margin-bottom: 1em;
274 font-size: 1em;
275 text-align: center;
276 width: 100%;
277 max-width: 600px;
278}
279</style>
From the code above, we fetch all the products in the cart and display them on the page. We also calculate the total price by summing up the prices of all the products in the cart.
Here is a demo of how the cart functionality works.
To wrap up the application, let’s create the application navbar component. Inside the Components
folder, create a file named Navbar.svelte
and add the following code to it:
1<script>
2import { Link, navigate } from "svelte-routing";
3
4function getCookie(name) {
5 const value = `; ${document.cookie}`;
6 const parts = value.split(`; ${name}=`);
7 if (parts.length === 2) return parts.pop().split(";").shift();
8}
9
10const email = getCookie("email");
11const username = getCookie("username");
12const userId = getCookie("userId");
13
14const handleLogout = () => {
15 document.cookie = "email=; path=/; max-age=0";
16 document.cookie = "username=; path=/; max-age=0";
17 document.cookie = "userId=; path=/; max-age=0";
18 navigate("/login");
19};
20</script>
21
22<nav>
23 <div class="nav-container">
24 <div class="nav-brand">
25 <Link to="/">My Store</Link>
26 </div>
27 <div class="nav-links">
28 <Link to="/">Home</Link>
29 {#if email}
30 <span>Welcome, {username}</span>
31 <Link to="/cart/w"><span class="meun">Cart</span></Link>
32 <a href="#" on:click|preventDefault={handleLogout}>Logout</a>
33 {:else}
34 <Link to="/login">Login</Link>
35 <Link to="/register">Register</Link>
36 {/if}
37 </div>
38 </div>
39 </nav>
40
41<style>
42nav {
43 background-color: #71b3bf;
44 color: white;
45 padding: 1em 0;
46 width: 100%;
47 box-sizing: border-box;
48}
49
50.nav-container {
51 display: flex;
52 justify-content: space-between;
53 align-items: center;
54 padding: 0 2em;
55 max-width: 1200px;
56 margin: 0 auto;
57}
58
59.nav-brand a {
60 color: white;
61 text-decoration: none;
62 font-size: 1.5em;
63 font-weight: bold;
64}
65
66.nav-links {
67 display: flex;
68 gap: 1em;
69}
70
71.nav-links a,
72.nav-links span {
73 color: white;
74 text-decoration: none;
75 font-size: 1em;
76}
77
78.nav-links span {
79 font-size: 1em;
80}
81.meun {
82 padding: 15px;
83}
84.meun:hover {
85 background-color: #71b3bf;
86}
87</style>
Here is a demo of our Cart functionality.
Next, let’s configure the application route. Inside the src
folder, open the App.svelte
file and replace its code with the following:
1<!-- App.svelte -->
2<script>
3import { Router, Link, Route } from "svelte-routing";
4import Home from "./Components/Home.svelte";
5import Login from "./Components/Login.svelte";
6import Register from "./Components/Register.svelte";
7import Product from "./Components/Product.svelte";
8import Cart from "./Components/Cart.svelte";
9</script>
10
11<main>
12 <Router>
13 <Route path="/" component="{Home}" />
14 <Route path="login" component="{Login}" />
15 <Route path="register" component="{Register}" />
16 <Route path="product/:id" component="{Product}" />
17 <Route path="cart/:id" component="{Cart}" />
18 </Router>
19 </main>
20
21<style>
22main {
23 text-align: center;
24 padding: 1em;
25 max-width: 100%;
26 margin: 0 auto;
27}
28</style>
Start the Svelte development server by running the command below in your terminal to test the application.
npm run dev
Next, open your browser and go to http://localhost:8080. You should see the application functioning as shown in the GIF image below.
In this tutorial, we learned the complete process, from setting up the backend with Strapi to developing the frontend with SvelteKit to create an e-commerce website.
Building an e-commerce website with SvelteKit and Strapi CMS offers a powerful and flexible solution for creating dynamic and responsive online stores.