├── .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 | <Text 93 | component="span" 94 | inherit 95 | variant="gradient" 96 | gradient={{ from: "pink", to: "yellow" }} 97 | > 98 | fully featured 99 | </Text>{" "} 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 | Pin Save EVM 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 | 7 | Vector 1 8 | Created with Sketch. 9 | 10 | 11 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 25 | 26 | 27 | 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 | --------------------------------------------------------------------------------