├── .github
└── dependabot.yml
├── .gitignore
├── .prettierrc.json
├── LICENCE
├── README.md
├── components
├── ColorSchemeToggle
│ └── ColorSchemeToggle.tsx
├── Hero
│ └── index.tsx
└── Layout
│ ├── Navbar
│ └── index.tsx
│ └── index.tsx
├── funding.json
├── next-env.d.ts
├── next.config.js
├── package.json
├── pages
├── _app.tsx
├── _document.tsx
└── index.tsx
├── public
├── SolidityLogo.svg
└── favicon.svg
├── styles
└── styles.css
├── tsconfig.json
└── yarn.lock
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: 'npm' # See documentation for possible values
4 | directory: '/' # Location of package manifests
5 | schedule:
6 | interval: 'daily'
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | *.env
29 | .env.local
30 | .env.development.local
31 | .env.test.local
32 | .env.production.local
33 |
34 | # vercel
35 | .vercel
36 | *.tsbuildinfo
37 |
38 | # storybook
39 | storybook-static
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": false
6 | }
7 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Pavel Fedotov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mantine Web3 Template
2 |
3 | 🌟 Kickstart Your Web3 Development Journey! 🌟
4 |
5 | Are you ready to dive into the world of Web3 development? Look no further! Check out the Mantine-Web3-Template, a powerful and streamlined setup designed to help you build modern, interactive applications using Mantine, RainbowKit, and Next.js.
6 |
7 | ## How to start
8 |
9 | Click `Use this template` button at the header of repository and
10 | create new repository with `@mantine` packages. Note that you have to be logged in to GitHub to generate template.
11 |
12 | ## Features
13 |
14 | This template comes with several essential features:
15 |
16 | - Server side rendering setup for Mantine
17 | - ESLint and TypeScript configurations for cleaner code
18 |
19 | Whether you're a seasoned developer or just starting your journey, this template will help you create incredible applications that leverage blockchain technology with ease.
20 |
21 | ## npm scripts
22 |
23 | ### Build and dev scripts
24 |
25 | - `dev` – start dev server
26 | - `build` – bundle application for production
27 | - `export` – exports static website to `out` folder
28 | - `analyze` – analyzes application bundle with [@next/bundle-analyzer](https://www.npmjs.com/package/@next/bundle-analyzer)
29 |
30 | ### Testing scripts
31 |
32 | - `typecheck` – checks TypeScript types
33 | - `lint` – runs ESLint
34 | - `prettier:check` – checks files with Prettier
35 | - `test` – runs `jest`, `prettier:check`, `lint` and `typecheck` scripts
36 |
37 | ### Other scripts
38 |
39 | - `prettier:write` – formats all files with Prettier
40 |
--------------------------------------------------------------------------------
/components/ColorSchemeToggle/ColorSchemeToggle.tsx:
--------------------------------------------------------------------------------
1 | import { ActionIcon, Group, useMantineColorScheme } from "@mantine/core";
2 | import { IconSun, IconMoonStars } from "@tabler/icons-react";
3 |
4 | export function ColorSchemeToggle() {
5 | const { colorScheme, toggleColorScheme } = useMantineColorScheme();
6 |
7 | return (
8 |
9 | toggleColorScheme()}
11 | size="xl"
12 | sx={(theme) => ({
13 | backgroundColor:
14 | theme.colorScheme === "dark"
15 | ? theme.colors.dark[6]
16 | : theme.colors.gray[0],
17 | color:
18 | theme.colorScheme === "dark"
19 | ? theme.colors.yellow[4]
20 | : theme.colors.blue[6],
21 | })}
22 | >
23 | {colorScheme === "dark" ? (
24 |
25 | ) : (
26 |
27 | )}
28 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/components/Hero/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | createStyles,
3 | Container,
4 | Title,
5 | Text,
6 | Button,
7 | rem,
8 | } from "@mantine/core";
9 |
10 | const useStyles = createStyles((theme) => ({
11 | root: {
12 | backgroundColor: "#11284b",
13 | backgroundSize: "cover",
14 | backgroundPosition: "center",
15 | backgroundImage:
16 | "linear-gradient(250deg, rgba(130, 201, 30, 0) 0%, #062343 70%), url(https://images.unsplash.com/photo-1451187580459-43490279c0fa?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1080&q=80)",
17 | paddingTop: `calc(${theme.spacing.xl} * 3)`,
18 | paddingBottom: `calc(${theme.spacing.xl} * 3)`,
19 | },
20 |
21 | inner: {
22 | display: "flex",
23 | justifyContent: "space-between",
24 |
25 | [theme.fn.smallerThan("md")]: {
26 | flexDirection: "column",
27 | },
28 | },
29 |
30 | image: {
31 | [theme.fn.smallerThan("md")]: {
32 | display: "none",
33 | },
34 | },
35 |
36 | content: {
37 | paddingTop: `calc(${theme.spacing.xl} * 2)`,
38 | paddingBottom: `calc(${theme.spacing.xl} * 2)`,
39 | marginRight: `calc(${theme.spacing.xl} * 3)`,
40 |
41 | [theme.fn.smallerThan("md")]: {
42 | marginRight: 0,
43 | },
44 | },
45 |
46 | title: {
47 | color: theme.white,
48 | fontFamily: `Greycliff CF, ${theme.fontFamily}`,
49 | fontWeight: 900,
50 | lineHeight: 1.05,
51 | maxWidth: rem(500),
52 | fontSize: rem(48),
53 |
54 | [theme.fn.smallerThan("md")]: {
55 | maxWidth: "100%",
56 | fontSize: rem(34),
57 | lineHeight: 1.15,
58 | },
59 | },
60 |
61 | description: {
62 | color: theme.white,
63 | opacity: 0.75,
64 | maxWidth: rem(500),
65 |
66 | [theme.fn.smallerThan("md")]: {
67 | maxWidth: "100%",
68 | },
69 | },
70 |
71 | control: {
72 | paddingLeft: rem(50),
73 | paddingRight: rem(50),
74 | fontFamily: `Greycliff CF, ${theme.fontFamily}`,
75 | fontSize: rem(22),
76 |
77 | [theme.fn.smallerThan("md")]: {
78 | width: "100%",
79 | },
80 | },
81 | }));
82 |
83 | export default function Hero() {
84 | const { classes } = useStyles();
85 | return (
86 |
87 |
88 |
89 |
90 |
91 | A{" "}
92 |
98 | fully featured
99 | {" "}
100 | React components library
101 |
102 |
103 |
104 | Build fully functional accessible web applications with ease –
105 | Mantine includes more than 100 customizable components and hooks
106 | to cover you in any situation
107 |
108 |
109 |
118 |
119 |
120 |
121 |
122 | );
123 | }
124 |
--------------------------------------------------------------------------------
/components/Layout/Navbar/index.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | createStyles,
3 | Text,
4 | Header,
5 | Container,
6 | Group,
7 | Burger,
8 | Paper,
9 | Transition,
10 | } from "@mantine/core";
11 | import { useToggle } from "@mantine/hooks";
12 | import { ConnectButton } from "@rainbow-me/rainbowkit";
13 | import Image from "next/image";
14 | import Link from "next/link";
15 | import { useRouter } from "next/router";
16 |
17 | const HEADER_HEIGHT = 60;
18 |
19 | const useStyles = createStyles((theme) => ({
20 | root: {
21 | position: "relative",
22 | zIndex: 1,
23 | },
24 |
25 | dropdown: {
26 | position: "absolute",
27 | top: HEADER_HEIGHT,
28 | left: 0,
29 | right: 0,
30 | zIndex: 0,
31 | borderTopRightRadius: 0,
32 | borderTopLeftRadius: 0,
33 | borderTopWidth: 0,
34 | overflow: "hidden",
35 |
36 | [theme.fn.largerThan("md")]: {
37 | display: "none",
38 | },
39 | },
40 |
41 | header: {
42 | display: "flex",
43 | justifyContent: "space-between",
44 | alignItems: "center",
45 | height: "100%",
46 | },
47 |
48 | links: {
49 | [theme.fn.smallerThan("md")]: {
50 | display: "none",
51 | },
52 | },
53 |
54 | burger: {
55 | [theme.fn.largerThan("md")]: {
56 | display: "none",
57 | },
58 | },
59 |
60 | link: {
61 | display: "block",
62 | lineHeight: 1,
63 | padding: "8px 12px",
64 | borderRadius: theme.radius.sm,
65 | textDecoration: "none",
66 | color:
67 | theme.colorScheme === "dark"
68 | ? theme.colors.dark[0]
69 | : theme.colors.gray[7],
70 | fontSize: theme.fontSizes.lg,
71 | fontWeight: 500,
72 |
73 | "&:hover": {
74 | backgroundColor:
75 | theme.colorScheme === "dark"
76 | ? theme.colors.dark[6]
77 | : theme.colors.gray[0],
78 | },
79 |
80 | [theme.fn.smallerThan("sm")]: {
81 | borderRadius: 0,
82 | padding: theme.spacing.md,
83 | },
84 | },
85 |
86 | linkActive: {
87 | "&, &:hover": {
88 | backgroundColor:
89 | theme.colorScheme === "dark"
90 | ? theme.fn.rgba(theme.colors[theme.primaryColor][9], 0.25)
91 | : theme.colors[theme.primaryColor][0],
92 | color:
93 | theme.colors[theme.primaryColor][theme.colorScheme === "dark" ? 3 : 7],
94 | },
95 | },
96 | }));
97 |
98 | interface NavbarProps {
99 | readonly links: { link: string; label: string }[];
100 | }
101 |
102 | export function Navbar({ links }: NavbarProps) {
103 | const [opened, toggle] = useToggle([false, true]);
104 | const { classes, cx } = useStyles();
105 | const router = useRouter();
106 | const items = links?.map((link) => (
107 |
108 |
113 | {link.label}
114 |
115 |
116 | ));
117 |
118 | return (
119 |
120 |
121 |
122 |
130 |
131 |
132 | {items}
133 |
134 |
135 |
141 | toggle()}
144 | className={classes.burger}
145 | size="sm"
146 | />
147 |
148 |
149 |
150 | {(styles) => (
151 |
152 | {items}
153 |
154 | )}
155 |
156 |
157 |
158 | );
159 | }
160 |
--------------------------------------------------------------------------------
/components/Layout/index.tsx:
--------------------------------------------------------------------------------
1 | import { AppShell } from "@mantine/core";
2 | import React from "react";
3 |
4 | import { Navbar } from "./Navbar";
5 |
6 | interface LayoutProps {
7 | children: JSX.Element;
8 | }
9 |
10 | const LayoutApp = ({ children }: LayoutProps) => {
11 | const links = [{ label: "Home", link: "/" }];
12 | return (
13 | }
15 | styles={{
16 | main: {
17 | paddingTop: 0,
18 | paddingRight: 0,
19 | paddingLeft: 0,
20 | paddingBottom: 0,
21 | minHeight: 0,
22 | },
23 | }}
24 | >
25 | {children}
26 |
27 | );
28 | };
29 |
30 | export default LayoutApp;
31 |
--------------------------------------------------------------------------------
/funding.json:
--------------------------------------------------------------------------------
1 | {
2 | "opRetro": {
3 | "projectId": "0x1a26744683129e6e36c790c4d9ed2aba3fa08b02a1a127c5805241b9a442de3a"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | // NOTE: This file should not be edited
5 | // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
6 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | reactStrictMode: true,
3 | };
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mantine-next-template",
3 | "version": "1.0.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "typecheck": "tsc --noEmit",
10 | "export": "next build && next export",
11 | "lint": "next lint",
12 | "prettier:check": "prettier --check \"**/*.{ts,tsx}\"",
13 | "prettier:write": "prettier --write \"**/*.{ts,tsx}\"",
14 | "test": "npm run prettier:check && npm run lint && npm run typecheck"
15 | },
16 | "dependencies": {
17 | "@emotion/react": "^11.13.3",
18 | "@emotion/server": "^11.11.0",
19 | "@mantine/carousel": "6.0.2",
20 | "@mantine/core": "6.0.2",
21 | "@mantine/hooks": "6.0.2",
22 | "@mantine/next": "6.0.22",
23 | "@mantine/notifications": "6.0.2",
24 | "@mantine/prism": "6.0.2",
25 | "@rainbow-me/rainbowkit": "^1.3.7",
26 | "@tabler/icons-react": "^3.31.0",
27 | "@tanstack/react-query": "5.69.0",
28 | "embla-carousel-react": "^7.1.0",
29 | "next": "15.2.4",
30 | "react": "18.3.1",
31 | "react-dom": "18.3.1",
32 | "viem": "^1.21.4",
33 | "wagmi": "^1.4.13"
34 | },
35 | "devDependencies": {
36 | "@babel/core": "^7.26.9",
37 | "@types/node": "^22.13.13",
38 | "@types/react": "18.3.13",
39 | "eslint": "^9.23.0",
40 | "prettier": "^3.5.3",
41 | "typescript": "5.7.3"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import "@/styles/styles.css";
2 | import "@rainbow-me/rainbowkit/styles.css";
3 |
4 | import type { AppProps as NextAppProps } from "next/app";
5 | import type { NextComponentType } from "next";
6 | import Head from "next/head";
7 | import { useState } from "react";
8 | import {
9 | MantineProvider,
10 | ColorScheme,
11 | ColorSchemeProvider,
12 | } from "@mantine/core";
13 | import { Notifications } from "@mantine/notifications";
14 | import { getDefaultWallets, RainbowKitProvider } from "@rainbow-me/rainbowkit";
15 | import { configureChains, createConfig, WagmiConfig } from "wagmi";
16 | import { mainnet, polygon, optimism, arbitrum } from "wagmi/chains";
17 | import { publicProvider } from "wagmi/providers/public";
18 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
19 |
20 | import LayoutApp from "@/components/Layout";
21 |
22 | type AppProps = NextAppProps & {
23 | pageProps: P;
24 | ColorScheme: ColorScheme;
25 | Component: NextComponentType & {
26 | getLayout?: (page: React.ReactElement) => React.ReactNode;
27 | };
28 | } & Omit, "pageProps">;
29 |
30 | export default function App({ Component, ColorScheme, pageProps }: AppProps) {
31 | const queryClient: QueryClient = new QueryClient();
32 | const [colorScheme, setColorScheme] = useState(ColorScheme);
33 |
34 | const toggleColorScheme = (value?: ColorScheme) => {
35 | const nextColorScheme =
36 | value || (colorScheme === "dark" ? "light" : "dark");
37 | setColorScheme(nextColorScheme);
38 | };
39 |
40 | const { chains, publicClient } = configureChains(
41 | [mainnet, polygon, optimism, arbitrum],
42 | [publicProvider()]
43 | );
44 |
45 | const { connectors } = getDefaultWallets({
46 | appName: "My RainbowKit App",
47 | projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_ID as string,
48 | chains,
49 | });
50 |
51 | const wagmiConfig = createConfig({
52 | autoConnect: true,
53 | connectors,
54 | publicClient,
55 | });
56 |
57 | return (
58 |
59 |
60 |
61 |
62 | Mantine NextJS RainbowKit example
63 |
64 |
65 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | );
79 | }
80 |
--------------------------------------------------------------------------------
/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import Document, {
2 | Html,
3 | Main,
4 | NextScript,
5 | DocumentContext,
6 | Head,
7 | } from "next/document";
8 |
9 | class MyDocument extends Document {
10 | static async getInitialProps(ctx: DocumentContext) {
11 | const initialProps = await Document.getInitialProps(ctx);
12 | return { ...initialProps };
13 | }
14 |
15 | render() {
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | );
28 | }
29 | }
30 |
31 | export default MyDocument;
32 |
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import { ColorSchemeToggle } from "@/components/ColorSchemeToggle/ColorSchemeToggle";
2 | import Hero from "@/components/Hero";
3 |
4 | export default function HomePage() {
5 | return (
6 |
7 |
8 |
9 |
10 | );
11 | }
12 |
--------------------------------------------------------------------------------
/public/SolidityLogo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
28 |
--------------------------------------------------------------------------------
/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/styles/styles.css:
--------------------------------------------------------------------------------
1 | a {
2 | text-decoration: none;
3 | }
4 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "baseUrl": ".",
18 | "paths": {
19 | "@/components/*": ["./components/*"],
20 | "@/public/*": ["./public/*"],
21 | "@/styles/*": ["./styles/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------