Includes magiclink + email auth
register for and create a new supabase database instance.
-
use
.env.sample
as a boilerplate. -
implement social auth
-
verify token refresh flow
-
update docs
Chakra UI requires the following packages:
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
Chakra UI is exposed to the application via the ChakraProvider
in app.js
:
import { ChakraProvider } from "@chakra-ui/react";
import { theme } from "@/chakra";
const App = ({ Component, pageProps, router }) => {
return (
<ChakraProvider resetCSS theme={theme}>
<Component {...pageProps} />
</ChakraProvider>
);
};
export default App;
**NOTE: ** the ChakraProvider
takes in the custom theme we've defined in the chakra
directory
The custom theme file extends chakra's default theme with the following custom modules:
-
Global Styles:
./chakra/global-styles.js
- defines all default styles related to the app, this includes: html, body, and some browser prefixes (such as focus-visible and default scrollbar)
-
Color Modes:
./chakra/color-modes.js
-
used to define light theme and dark theme colors
-
NOTE use the custom hook
use-color
to implement color schemes -
Usage:
import { useColor } from "@/chakra/hooks/use-color"; export const ErrorTag = ({children, ...rest }) => { const { color } = useColor(); return ( <Box bg={color("danger")} {...rest}> {children} </Box> ) }
-
-
Fonts:
./chakra/fonts.js
-
defines the custom fonts along with the properties used to reference them with in chakra
export const fonts = { heading: "'Noto Serif', Iowan Old Style, Apple Garamond, Baskerville, Times New Roman, Droid Serif, Times, Source Serif Pro, serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol", body: "'Noto Sans', --apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif", };
-
Usage:
<Text fontFamily='body'> {lead} </Text>
-
-
layerStyles:
./chakra/layerStyles/bars.js
-
use to allow themeing for header and footer placeholders in the app scaffold
🔴 (-- see scaffold section below for more info)
-
-
layouts :
./chakra/layouts
- used to expose different layouts if needed in the app -- currently renders the scaffold
- ☑️ TODO: merge layouts and scaffold components
./chakra/components/*
includes components we've modified based on chakra components and components specifically used by the scaffold.
- Alert can be used via the
useAlert
hook. (--seeuseAlert
for more info)
- Mainly used to display errors on the front end, gets rendered automatically by the Error Context Provider.
- Can be globally turned off using the options in
./app.config.js
- Uses
textStyles
to override the default styling for all markdown content. - Provides overrides for all text components (Text, Heading, etc)
- used to switch between light and dark mode on the client side.
-
used for debugging data fetching. renders a button (Show JSON) which present data in a modal when clicked.
-
Usage:
import { ShowJson } from "@/chakra/components/show-json"; export const Responser = ({}) => { const [data, setData] = useState(initialData ?? null); useEffect(async () => { if (initialData || data) return; const response = await fetch(`https:/....`); if (response?.data) { setData(response?.data); logger.success("responser", response?.data); } else logger.error("responser"); }, []); return <ShowJson data={data} />; }
- Instead of using chakra's spinner and worrying about centering it inside of each component this will automatically display centered in any component.
-
Scaffold consists of several parts.
-
Scaffod Provider - implements providers for several contexts (Error, Toast, Messenger, ..) (-- see scaffold context for more info)
-
PageGrid - component used to render the page structure creating for placeholders for header, footer and main content area.
-
Placeholders can be populated by defining the component each placeholder should render in
./app.config.js
-
Each of the place holders are defined in
@chakra/components/structure/bars
- This file exposes two components
HeaderComponent
&FooterComponent
- They are used to render the your custom header and footer which you can define in
./app.config.js
- This file exposes two components
-
USAGE: (how to compose your custom header from
./app.config.js
)appConfig.scaffold = { defaults: { header: { component: Header, props: { HeaderComponent: HeaderContent }, }, footer: { component: Footer, props: { FooterComponent: FooterContent }, }, }, infoBlock: { id: "infoblock", Component: FooterInfo, }, theme: "default", };
-
-
Component is rendered at the root of the application in
_app.js
import MessageRouter from '@/components/message-router'; const App = ({ Component, pageProps, router }) => { return ( <MessageRouter asPath={router.asPath}> {children} </MessageRouter> ) }
-
on any route change you can add the query string:
?error="" ?success=""
any message passed along within the strings will be intercepted by the MessageRouter on any redirect event. If the query string includes the keyword "error" then an error notification is shown if it's success a success notification is shown. For any other queries MessageRouter will simply ignore the query and will essentially get bypassed.
NOTE: You must ensure you're allowing notifications via
app.config.js
options or via localStorage.
-
Usage:
import { useDisclosure } from "@chakra-ui/react"; import {ChModal} from "@/components/modal" const Example = ({children, ...rest}) => { const { isOpen, onOpen, onClose } = useDisclosure(); return ( <CHModal isOpen={isOpen} onOpen={onOpen} onClose={onClose} hasSubmit={true} // set true if the modal contains a form component title={ <Heading as='h1' fontSize='5xl' textAlign='center' color='inherit' pt={12} > Welcome Back! </Heading> } /> ) }
./chakra/contexts/index.js
- composes all contexts into a single Provider
object that is then consumed and rendered by the ScaffoldContext
-
Renders the ErrorTag component to display notifications at the top of the page.
-
Manages toast notifications primarily for errors by exposing the
setError
function which can be used from theuseErrorDispatch()
hook -
-- provider rendered via scaffold context
-
Usage:
import { useErrorDispatch } from "@/chakra/contexts/error-context"; export const Example = ({ children, ...rest }) => { const { setError } = useErrorDispatch(); const handleSubmit = async (e) => { e.preventDefault(); e.persist(); const response = await fetch(`http://...`) if(response.error) setError(response.error) } /*...*/ }
-
used to dynamically render modals with passed in data
-
exposed via
useModalDispatch()
hook -
Usage: -- ☑️ to be updated
- exposes the following providers: modal, modalForm, sidebar, toasts, errors
- also renders NProgress used as a page loader defined at
./components/nprogress
- also exposes the
useScaffold()
hook - used to update header/footer and manage options via./app.config
- also sets options from
./app.config
to local storage under the key: "options"
-
used by
ErrorContext
under the hood to display error toasts. -
exposes the
useToastDispatch()
hook. -
Usage:
import { useToastDispatch } from "@/chakra/contexts/toast-context"; export const Example = ({children, ...rest}) => { const { setMsg } = useToastDispatch(); setMsg({ description: parsedMsg, position: "bottom-right", title: "Error", status: "error", }); /*...*/ }
-
Usage:
import { useColor } from "@/chakra/hooks/use-color"; export const Example = ({children, ...rest}) => { const { color } = useColor(); return ( <Box bg={color("bg")}> {children} </Box> ); }
-
composes chakra's default Alert component into a reusable modal
-
Exposes the Alert component that renders the message dialog and a handleConfirm callback
-
use the handleConfirm callback to execute any action after user has confirmed their action in the modal dialog.
-
Usage: ☑️ TODO update usage example
-
Using chakra to compose icons and shapes
-
First take the svg icon and convert its path to an object format:
const paths = { add: { viewBox: "0 0 56 56", d: [ "M46.667 4.667h-28A4.671 4.671 0 0014 9.333v28A4.671 4.671 0 0018.667 42h28a4.671 4.671 0 004.666-4.667v-28a4.671 4.671 0 00-4.666-4.666zm-28 32.666v-28h28l.004 28H18.667z", "M9.333 18.667H4.667v28a4.671 4.671 0 004.666 4.666h28v-4.666h-28v-28zM35 14h-4.667v7h-7v4.667h7v7H35v-7h7V21h-7v-7z", ], }, }
-
then use the CustomIcon or CustomShape components (
@/chakra/icons/custom-icon.js
) -
Usage: as standalone icon
import { CustomIcon } from "@/chakra/icons/custom-icon"; export const Example = ({children, ...rest}) => { return ( <CustomIcon icon='add' color='gray.600' /> ) }
-
Usage: as Icon Button
import { CustomIcon } from "@/chakra/icons/custom-icon"; export const Example = ({children, ...rest}) => { const [isSubmitting, setIsSubmitting] = useState(false) return ( <IconButton type='submit' colorScheme='blue' aria-label='send email' mr={1} icon={<CustomIcon icon='plane' color='gray.600' />} isLoading={isSubmitting} /> ) }
-
Usage via ToolIcon component
./@chakra/icons/tool-icon.js
import { CustomIcon } from "@/chakra/icons/custom-icon"; export const Example = ({children, ...rest}) => { const [isLoading, setIsLoading] = useState(false) return ( <IconButton isLoading={isLoading} isDisabled={isLoading} type='submit' icon={ <ToolIcon icon={"disk"} tipLabel={`Update ${unPluralize(tabView)}`} /> } size='lg' mr={2} mt={-4} p={2} stroke='gray.100' /> ) }
Used for interaction animations also required by chakra for transition components, popovers and modals
-
./components/motion-box.js
-
Usage:
import { Box, forwardRef } from "@chakra-ui/react"; import { motion, isValidMotionProp } from "framer-motion"; export const MotionBox = motion( forwardRef((props, ref) => { const chakraProps = Object.fromEntries( // NOTE: does not pass framer props to DOM element Object.entries(props).filter(([key]) => !isValidMotionProp(key)) ); return <Box ref={ref} {...chakraProps} />; }) );
Package that adds necessary css prefixes that keeps accessibility in tact while removing the border outlines that browsers natively apply to buttons and links. This package is consumed at the top of the global stylesheet located at @/chakra/global-styles.js
import "focus-visible/dist/focus-visible";
export const styles = {
global: (props) => ({
".js-focus-visible": {
/*
This will hide the focus indicator if the element receives focus with the mouse, but it will still show up on keyboard focus thus keeping accessibility in tact.
*/
outline: "none",
boxShadow: "none",
},
}
Page loader animation component. Shows a loading indicator at the top of each page, that fires on each route change event. Component is defined in @/components/nprogress.js
NOTE: requires lodash's debounce method and uses styled-jsx to apply the necessary styles globally.
- ☑️ TODO: add styles for nprogress to global styles.
./app.config.js
is a file located at the root of the project directory. The purpose of this file is to allow for modularity in the application, without having to use global context or redux.
The functionality is still crude and is very much a work in progress. NOTE: this topic is certainly open to discussion and criticism, as it is still in it's infancy.
We'll cover the file in as much detail as needed, the file itself exposes a single module appConfig
, but is made up of two separate parts.
- App Config - [details, routes, options]
- Scaffold Config - [defaults]
- Also there's a third part not yet implemented for SEO
appConfig.seo
The first part App Config
- is used to set some basic details about the app. If you've ever used gatsby this concept is loosely based on the gatsby-config.js
files used to configure gatsby projects.
const appConfig = {
details: {
title: "Falcon Driving School",
description: "Hudson County's Best Way To Learn How To Drive!",
siteUrl: process.env.NEXT_PUBLIC_URL,
cdnUrl: process.env.NEXT_PUBLIC_CDN_URL,
},
routes: {
home: "/",
about: "about",
services: "services",
contact: "contact",
},
options: {
toasts: { show: true },
errors: { show: true, config: { tags: true } },
tips: { show: false },
},
};
details
- App details include title, description and urls.routes
- define all necessary routes for the application - instead of creating a routes file -- (this may not even be necessary in a next.js app and probably can be removed)options
- sets up the configuration globally for things like error notifications and tooltips -- these options will also be persisted to local storage and can be updated from there giving you app-wide control over when to show or not show certain notifications.
- NOTE: these could be set up individually based on your environment so you could choose to only show error notifications in development virtually supressing them in production.
The next part Scaffold Config
is where we can register the components we'd like rendered on each page of our app. This helps us define and customize foundational components like the Header, Footer, and other persistent components.
import { Header, Footer } from "@/chakra/components/structure/bars";
import { HeaderContent } from "@/components/structure/header-content";
import { FooterContent } from "@/components/structure/footer-content";
import { FooterInfo } from "./components/structure/footer-info";
appConfig.scaffold = {
defaults: {
header: {
component: Header,
props: { HeaderComponent: HeaderContent },
},
footer: {
component: Footer,
props: { FooterComponent: FooterContent },
},
},
infoBlock: {
id: "infoblock",
Component: FooterInfo,
},
theme: "default",
};
NOTE: there's also the ability to define a theme here, this can be used throughout the application, and can be updated via local storage
The final part is the SEO Config
which has not been implemented as of yet, and will theoretically allow us to configure the SEO of each individual page on the fly.
The starter includes various helper functions meant to make development easier. You can find all the helper functions in the @/utils
directory. They should be pretty well documented within the comments for each function. If the usage of any is unclear, feel free to do a search with shift+cmd+f to look for instances used throughout the application.