A template built on the following stack:
- Next.js
- Typescript
- Chakra UI for styled components
- Framer Motion for animations
- Cypress for e2e, component and unit testing
- Formik for form validation
- React-icons for icons
- SVGR for SVG
- Sanity for content management (optional)
To start:
npm install
npm run dev
https://ahsan-nextjs-template.vercel.app/
The template contains common components that I use in my projects. You can delete them if you don't need them.
The component files follow the following conventions (all files and directories are in camelCase):
π folderName
π componentName.ts # implementation
π index.js # to export components and types
π types.ts # for types and interfaces
π animations.ts # for animations
π # other files as required...
There is a script to generate components with all the required files. Use it like this:
npm run gen-component ComponentName
A utility component that contains a Head
, a Nav
and a Footer
.
<Page title="About">
<p>Page content</p>
</Page>
title: string
: The title of the pagedescription: string
: The description of the page to be used in meta tagsimageUrl: string
: The URL of the image to use in the meta tagsrobots: string
: The robots meta tag value
An animated variant with enter and exit animations is also available:
<AnimatedPage title="About">
<p>Page content</p>
</AnimatedPage>
It has a fade animation by default. The animations are powered by Framer Motion. You can specify your own animation using the animationVariants
prop.
<AnimatedPage
title="About"
animationVariants={{
enter: { opacity: 0 },
exit: { opacity: 0 },
animate: { opacity: 1 },
}}
>
<p>Page content</p>
</AnimatedPage>
animationVariants: PageAnimation
: The animation variants to use for the page. Includes threeVariant
objects:enter
,exit
andanimate
.animateContentOnly: boolean
: Whether to animate the content only or the whole page (content + footer + nav).
A simple nav component with a logo and links.
<Nav />
A collapsible, full-screen nav for mobile devices is also available:
<CollapsibleNav />
To switch to the collapsible navbar on mobile (this has already been done by default in the template):
const isSmallScreen = useBreakpointValue({
base: true,
xs: true,
md: false,
});
return isSmallScreen ? <CollapsibleNav /> : <Nav />;
You can edit the breakpoints in theme/core/breakpoints.ts
A wrapper around the Chakra Input
component. It adds a label, an error message as well as options to change label and error styles. There is also a FormTextArea
component for text area input.
label: string
: The label texterror: string
: The error messagelabelPosition : 'top' | 'placeholder'
:top
is the conventional label position above the input,placeholder
puts the label as a placeholder inside the input.errorPosition : 'bottom' | 'icon'
:bottom
puts the error message below the input,icon
puts the error message inside the input as an icon.
const [value, setValue] = useState('')
...
return (
<FormInput
label="Name"
onChange={(event) => setValue(event.target.value)}
value={value}
isInvalid={value === ''}
error="This field is required" />
<FormTextArea label="Message" />
)
A wrapper around the FormInput
component that uses the Formik library. There is also FormikTextArea
for text area input.
fieldName: string
: The name of the field in the formik statelabel: string
: An optional label. If not provided, thefieldName
is used as the label (capitalized)
<Formik
initialValues={{ name: "", password: "", confirmPassword: "" }}
...
>
{() => (
<FormikInput name="name"/>
<FormikInput name="password"/>
<FormikInput name="confirmPassword" label="Confirm Password"/>
<FormikTextArea name="message"/>
)}
</Formik>
A wrapper around NextLink
and a
.
href: string
: The URL to link toisExternal: boolean
: Whether the link is external or not. External links are opened in new tabs.
<Anchor href="https://www.google.com" isExternal>
...
</Anchor>
A wrapper around NextHead
. Adds a title as well as common meta tags such as robots
, og
, twitter
etc. Customize it to your needs. It is already included in the Page
component.
<Head
title="About"
description="This is my about page"
imageUrl="/public/img.png"
robots="noimageindex"
/>
title: string
: The title of the pagedescription: string
: The description of the page to be used in meta tagsimageUrl: string
: The URL of the image to use in the meta tagsrobots: string
: The robots meta tag value
A basic footer with a logo, socials and a copyright message. It is already included in the Page
component.
<Footer />
A clickable plain text button with an underline animation on hover.
<TextButton label="Click Me" onClick={()=>{...}}>
A wrapper around Anchor
and TextButton
. If the current page is the same as the link, the link is underlined.
<Link label="About" href="/about">
A stack of social icons. Customize the icons and hrefs to your needs.
<Socials direction="column" spacing="1rem" />
If you quickly need a contact form, you can bootstrap a contact component with:
npm run add-contact
This adds
nodejs-nodemailer-outlook
to send email- A
Contact
component in/components
that contains some basic fields using Formik - A
contact
api endpoint in/api
that sends the email - A
ContactData
interface in/interfaces
that defines the contact data
The email is sent to your email address through an intermediary address. This is a rudimentary method. You should consider replacing it with a email service like Mailgun or SendGrid.
To specify the required email info, copy the env.local.example
file to a env.local
file and fill out the variables.
The project already has Chakra's recommended theme directory structure set up.
π theme
π index.js # theme entrypoint
π styles.js # global style overrides
π core
π borders.js # border overrides
π colors.js # color overrides
π # and so on...
π components
π button.js # button overrides
π input.js # input overrides
π # and so on...
Check out the Chakra docs on how to customize the theme. Theme typings have also already been set up.
You can use any CMS with this template. There is a script that sets up a Sanity CMS for you. Use it like this:
npm run init-cms
It sets up the Sanity studio inside the cms
directory. It also sets up types generation for your schema. You can update the types like so:
npm run gen-sanity-types
To start the studio:
npm run cms
To use the client:
import sanity from "@/cms/sanityClient";
// `posts` is a schema
const posts = await sanity.getAll("posts");
Example usage in getStaticProps:
import sanity from "@/cms/sanityClient";
import { UnwrapPromise } from "@/types/unwrapPromise";
export const getStaticProps = async () => {
const posts = await sanity.getAll("posts");
return {
props: {
posts,
},
};
};
// Get typings for the static props
type Props = UnwrapPromise<ReturnType<typeof getStaticProps>>["props"];
const Articles: NextPage<Props> = (props) => {
return(
{props.posts.map((post) => (
<Heading>
{post.title}
</Heading>
<Text>
{post.body}
</Text>
))}
)
}
The default client is a sanity-codegen client that allows us to use typescript for our schemas. You can use any other client you want like next-sanity.
Cypress is installed as the default testing tool for e2e, component and unit testing. To start cypress:
npm run dev # Your project should be running for e2e tests
npm run e2e
Check out the docs for more info on how to use cypress.
Framer Motion is used for animations. The AnimatePresence
component that allows exit animations has already been setup in /pages/_app.tsx
.
SVGR is used to import SVG files. To import an SVG file, add it to the /public
directory (ideally in the svg
folder):
π public
π svg
π logo.svg
Then, in your component, import the SVG file:
import Logo from "@/public/svg/logo.svg";
...
<Logo width="5rem"/>
React Icons is used for icons. You can search for icons here and click to copy the icon name. Then import them like:
import { FaTwitter } from "@/icons/fa";
import { IoIosArrowForward } from "@/icons/io";
import { MdArrowBack } from "@/icons/md";
// And so on...
...
<FaTwitter/>
<IoIosArrowForward/>
<MdArrowBack/>
To use the Chakra theme with the icons:
import { FaTwitter } from "@/icons/fa";
import { Icon } from "chakra/react"
...
<Icon
as={FaTwitter}
width="1.5rem"
height="1.5rem"
color="brand.primary"
/>
Github Actions is used for CI. A run-cypress-tests.yml
file is included in .github/workflows/
that runs cypress tests on each push.
Another workflow file is added when you run npm run init-cms
in .github/workflows/deploy-cms.yml
that deploys the Sanity CMS on each push. You can customize it to your needs.