├── .babelrc.js
├── .gitignore
├── README.md
├── components
├── Contact.tsx
├── Footer.tsx
├── Header.tsx
├── Product.tsx
└── ProductList.tsx
├── next.config.js
├── package-lock.json
├── package.json
├── pages
└── index.tsx
├── static
├── aquarium.svg
├── crowntail.jpg
├── dragonscale.jpg
├── favicon.ico
├── halfmoon.jpg
├── logo.svg
└── veiltail.jpg
└── styles.scss
/.babelrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['next/babel', '@zeit/next-typescript/babel']
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .next
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Next.js E-Commerce Tutorial: Quick Shopping Cart Integration
2 |
3 | 
4 |
5 | The creation of tools such as Next.js that successfully simplified React frontend development.
6 |
7 | Here, I want to explore what Next.js can do for e-commerce.
8 |
9 | Steps:
10 |
11 | - Set up a Next.js development environment
12 | - Create new pages & components
13 | - Fetch data & import components
14 | - Add a shopping cart to a Next.js app
15 | - Style & deploy the app
16 |
17 | > [Read the full tutorial](https://snipcart.com/blog/next-js-ecommerce-tutorial)
18 |
19 | > [See the live demo](https://snipcart-nextjs.herokuapp.com/)
20 |
21 | Enjoy folks!
22 |
--------------------------------------------------------------------------------
/components/Contact.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function Contact() {
4 | return (
5 |
6 |
Any questions? Contact us.
7 |
We're looking forward to hearing from you. Feel free to contact us if you have any questions!
8 |
9 |
10 |
11 |
12 | )
13 | }
--------------------------------------------------------------------------------
/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | export default function Footer(){
2 | return (
3 |
11 | )
12 | }
--------------------------------------------------------------------------------
/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 |
3 | export default function Header() {
4 | return (
5 |
19 | )
20 | }
--------------------------------------------------------------------------------
/components/Product.tsx:
--------------------------------------------------------------------------------
1 | import {withRouter, RouterProps} from 'next/router'
2 |
3 | export interface IProduct {
4 | id: string
5 | name: string
6 | price: number
7 | url: string
8 | description: string
9 | image: string
10 | }
11 |
12 | interface IProductProps {
13 | product: IProduct
14 | router: RouterProps
15 | }
16 |
17 | const Product = (props: IProductProps) => {
18 | return (
19 |
20 |
{props.product.name}
21 |
{props.product.description}
22 |

23 |
24 |
${props.product.price.toFixed(2)}
25 |
34 |
35 |
36 | )
37 | }
38 |
39 | export default withRouter(Product)
--------------------------------------------------------------------------------
/components/ProductList.tsx:
--------------------------------------------------------------------------------
1 | import Product, { IProduct } from "./Product"
2 |
3 | interface IProductListProps {
4 | products: IProduct[]
5 | }
6 |
7 | const ProductList = (props: IProductListProps) => {
8 | return (
9 |
10 | {props.products.map((product, index) =>
)}
11 |
12 | )
13 | }
14 |
15 | export default ProductList
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | const withTypescript = require('@zeit/next-typescript')
2 | const withSass = require('@zeit/next-sass')
3 |
4 | module.exports = withTypescript(withSass());
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "snipcart-nextjs",
3 | "version": "1.0.0",
4 | "description": "snipcart-nextjs",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "next",
8 | "build": "next build",
9 | "start": "next start"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/snipcart/snipcart-nextjs.git"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/snipcart/snipcart-nextjs/issues"
20 | },
21 | "homepage": "https://github.com/snipcart/snipcart-nextjs#readme",
22 | "dependencies": {
23 | "@types/next": "^8.0.5",
24 | "@types/react": "^16.8.17",
25 | "@types/react-dom": "^16.8.4",
26 | "@zeit/next-sass": "^1.0.1",
27 | "@zeit/next-typescript": "^1.1.1",
28 | "next": "^8.1.0",
29 | "node-sass": "^4.12.0",
30 | "react": "^16.8.6",
31 | "react-dom": "^16.8.6"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import Header from "../components/Header"
2 | import ProductList from "../components/ProductList"
3 | import { IProduct } from "../components/Product"
4 | import Footer from "../components/Footer"
5 | import Contact from "../components/Contact"
6 | import Head from "next/head"
7 |
8 | import "../styles.scss"
9 |
10 | interface IIndexProps {
11 | products: IProduct[]
12 | }
13 |
14 | const Index = (props: IIndexProps) => {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
REDISCOVER
28 |
Fishkeeping
29 |
An exclusive collection of bettas available for everyone.
30 |
31 |
32 |
33 |
34 |
35 |
36 | )
37 | }
38 |
39 | Index.getInitialProps = async () => {
40 | return {
41 | products: [
42 | {id: "nextjs_halfmoon", name: "Halfmoon Betta", price: 25.00, image: "../static/halfmoon.jpg", description: "The Halfmoon betta is arguably one of the prettiest betta species. It is recognized by its large tail that can flare up to 180 degrees."} as IProduct,
43 | {id: "nextjs_dragonscale", name: "Dragon Scale Betta", price: 35, image: "../static/dragonscale.jpg",description: "The dragon scale betta is a rarer and higher maintenance fish. It is named by its thick white scales covering his sides that looks like dragon scale armor."} as IProduct,
44 | {id: "nextjs_crowntail", name: "Crowntail Betta", price: 7.50, image: "../static/crowntail.jpg", description: "The crowntail is pretty common, but interesting none the less. It's recognized by the shape of its tail that has an appearance of a comb."} as IProduct,
45 | {id: "nextjs_veiltail", name: "Veiltail Betta", price: 5.00, image: "../static/veiltail.jpg", description: "By far the most common betta fish. You can recognize it by its long tail aiming downwards."} as IProduct,
46 | ]
47 | }
48 | }
49 |
50 | export default Index
--------------------------------------------------------------------------------
/static/aquarium.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/crowntail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snipcart/snipcart-nextjs/0a8c9e7268fbf047103cfbc3de4d395bbf2c22e1/static/crowntail.jpg
--------------------------------------------------------------------------------
/static/dragonscale.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snipcart/snipcart-nextjs/0a8c9e7268fbf047103cfbc3de4d395bbf2c22e1/static/dragonscale.jpg
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snipcart/snipcart-nextjs/0a8c9e7268fbf047103cfbc3de4d395bbf2c22e1/static/favicon.ico
--------------------------------------------------------------------------------
/static/halfmoon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snipcart/snipcart-nextjs/0a8c9e7268fbf047103cfbc3de4d395bbf2c22e1/static/halfmoon.jpg
--------------------------------------------------------------------------------
/static/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/static/veiltail.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snipcart/snipcart-nextjs/0a8c9e7268fbf047103cfbc3de4d395bbf2c22e1/static/veiltail.jpg
--------------------------------------------------------------------------------
/styles.scss:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800');
2 |
3 | $color-purple: #9094FF;
4 | $color-light-grey: #EBEBEB;
5 | $color-dark: #343434;
6 | $max-width: 1000px;
7 |
8 | @mixin centered {
9 | width: 100%;
10 | max-width: $max-width;
11 | margin-left: auto;
12 | margin-right: auto;
13 |
14 | @include smallerThanDesktop {
15 | padding-left: 20px;
16 | padding-right: 20px;
17 | }
18 | }
19 |
20 | @mixin mobile {
21 | @media (max-width: '600px') {
22 | @content;
23 | }
24 | }
25 |
26 | @mixin smallerThanDesktop {
27 | @media (max-width: '1000px') {
28 | @content;
29 | }
30 | }
31 |
32 | * {
33 | box-sizing: border-box;
34 | font-family: 'Open Sans', sans-serif;
35 | font-size: 16px;
36 | }
37 |
38 | html, body {
39 | margin: 0;
40 | padding: 0;
41 | }
42 |
43 | .app {
44 | min-height: 100vh;
45 | display: grid;
46 | grid-template-rows: auto 1fr auto;
47 | }
48 |
49 | .header {
50 | @include centered;
51 |
52 | display: flex;
53 | padding-top: 20px;
54 | padding-bottom: 20px;
55 |
56 | &__summary {
57 | font-weight: bold;
58 | display: flex;
59 | justify-content: center;
60 | align-items: center;
61 | margin-left: auto;
62 | }
63 |
64 | &__price {
65 | color: $color-purple;
66 | margin-left: 10px;
67 | }
68 |
69 | &__logo {
70 | height: 38px;
71 | }
72 |
73 | &__title {
74 | margin: 0;
75 | padding: 0;
76 | font-size: 24px;
77 | margin-left: 20px;
78 | }
79 | }
80 |
81 | .main {
82 |
83 | }
84 |
85 | .background-image {
86 | position: absolute;
87 | opacity: 0.02;
88 | left: -10vw;
89 | top: -10vh;
90 | height: 75vh;
91 | transform: rotate(20deg);
92 | pointer-events: none;
93 | }
94 |
95 | .promotional-message {
96 | margin-bottom: 100px;
97 | margin-top: 25px;
98 | text-align: center;
99 |
100 | h3 {
101 | font-size: 20px;
102 | line-height: normal;
103 | text-align: center;
104 | letter-spacing: 0.4em;
105 | text-transform: uppercase;
106 | margin: 0;
107 | }
108 |
109 | h2 {
110 | font-size: 100px;
111 | @include mobile { font-size: 50px; }
112 | margin: 0;
113 | color: $color-purple;
114 | }
115 | }
116 |
117 | .product-list {
118 | @include centered;
119 | }
120 |
121 | .product {
122 | display: grid;
123 | width: 100%;
124 |
125 | display: grid;
126 | grid-template-areas:
127 | "title title image"
128 | "description description image"
129 | "button button image"
130 | ". . image";
131 | grid-template-columns: 1fr 1fr 3fr;
132 | margin-bottom: 100px;
133 | grid-column-gap: 100px;
134 |
135 | &:nth-of-type(odd) {
136 | grid-template-areas:
137 | "image title title"
138 | "image description description"
139 | "image button button"
140 | "image . .";
141 | grid-template-columns: 3fr 1fr 1fr;
142 |
143 | @include mobile {
144 | grid-template-areas:
145 | "image image "
146 | "title title "
147 | "description description"
148 | "button button ";
149 | grid-template-columns: 1fr 1fr;
150 |
151 | img {
152 | height: 300px;
153 | width: 100%;
154 | margin-bottom: 30px;
155 | }
156 | }
157 | }
158 |
159 | &__title {
160 | margin: 0;
161 | grid-area: title;
162 | font-size: 32px;
163 | font-weight: bold;
164 | }
165 |
166 | &__description {
167 | grid-area: description;
168 | line-height: 1.75rem;
169 | min-height: 175px;
170 | @include mobile {
171 | min-height: 0px;
172 | }
173 | }
174 |
175 | &__price {
176 | grid-area: price;
177 | font-size: 28px;
178 | font-weight: bold;
179 | }
180 |
181 | &__image {
182 | grid-area: image;
183 | width: 100%;
184 | height: 100%;
185 | object-fit: cover;
186 | border-radius: 4px;
187 | box-shadow: 0px 18.025px 43.775px rgba(0, 0, 0, 0.25);
188 | }
189 |
190 | &__price-button-container {
191 | display: flex;
192 | grid-area: button;
193 | }
194 |
195 | &__button {
196 | margin-left: 30px;
197 | font-size: 14px;
198 | font-weight: bold;
199 | border-radius: 4px;
200 | padding: 6px;
201 | padding-left: 20px;
202 | padding-right: 20px;
203 | border: none;
204 | background-color: $color-purple;
205 | color: white;
206 | position: relative;
207 |
208 | &:hover {
209 | transition: 0.2s all;
210 | &:before {
211 | transform: scale(1.15, 1.4);
212 | }
213 | }
214 |
215 | &:before {
216 | content: ' ';
217 | position: absolute;
218 | background-color: $color-purple;
219 | top: 0;
220 | left: 0;
221 | border-radius: 4px;
222 | width: 100%;
223 | height: 100%;
224 | opacity: 0.4;
225 | z-index: -1;
226 | transform: scale(1);
227 | transition: all 0.3s cubic-bezier(0.16, 0.8, 0.66, 1.54);
228 | }
229 | }
230 |
231 | @include mobile {
232 | grid-template-areas:
233 | "image image "
234 | "title title "
235 | "description description"
236 | "button button ";
237 | grid-template-columns: 1fr 1fr;
238 |
239 | img {
240 | height: 300px;
241 | width: 100%;
242 | margin-bottom: 30px;
243 | }
244 | }
245 | }
246 |
247 | .contact {
248 | text-align: center;
249 | background-color: $color-light-grey;
250 | padding-top: 100px;
251 | padding-bottom: 100px;
252 | &__title {
253 | font-size: 36px;
254 |
255 | .colored {
256 | font-size: inherit;
257 | color: $color-purple;
258 | }
259 | }
260 |
261 | &__paragraph {
262 | @include centered;
263 | font-weight: 600;
264 | }
265 |
266 | button {
267 | margin-top: 15px;
268 | font-size: 14px;
269 | font-weight: bold;
270 | border-radius: 4px;
271 | padding: 12px;
272 | padding-left: 20px;
273 | padding-right: 20px;
274 | border: none;
275 | background-color: $color-dark;
276 | color: white;
277 | position: relative;
278 | z-index: 99;
279 |
280 | &:hover {
281 | transition: 0.2s all;
282 | &:before {
283 | transform: scale(1.15, 1.4);
284 | }
285 | }
286 |
287 | &:before {
288 | content: ' ';
289 | position: absolute;
290 | background-color: $color-dark;
291 | top: 0;
292 | left: 0;
293 | border-radius: 4px;
294 | width: 100%;
295 | height: 100%;
296 | opacity: 0.4;
297 | z-index: -1;
298 | transform: scale(1);
299 | transition: all 0.3s cubic-bezier(0.16, 0.8, 0.66, 1.54);
300 | }
301 | }
302 | }
303 |
304 | .footer {
305 | color: white;
306 | background: linear-gradient(90deg, #707070 0%, #474747 100%);
307 | &__left {
308 | margin-left: auto;
309 | }
310 |
311 | a {
312 | color: white;
313 | }
314 | p {
315 | display: flex;
316 | margin: 0;
317 | @include centered;
318 | padding-top: 20px;
319 | padding-bottom: 20px;
320 | }
321 | }
--------------------------------------------------------------------------------