CodeWithSwiz is a twice-a-week live series. Like a podcast with video and fun hacking. Focused on experiments. Join live Wednesdays and Sundays
In this episode we tried NextJS, a React webapp framework, and built a small app. You can see it live at swiz-cms.vercel.app.
The ultimate goal for this app is to help folks create markdown articles. Copypasta is fine, but I always forget to change the title, write a description, add categories, and create a social card.
In 55min we went from "Never tried NextJS before" to a working app with a design system, forms, static rendering, and talking to an AWS Lambda to make cards. Not bad.
Initial setup
npx create-next-app swiz-cms
That creates a plain ol' NextJS app. You get pleasant default styling, support for file-based routing, and server-side-rendering/generation out of the box.
Means you can add a new page by creating a file in pages/file.js
. Anything in there becomes a page.
You get support for building APIs out of the box via pages/api
, but we didn't try that on this stream. Next time 😉
Wish you got MDX support out of the box. Makes for a better experience writing landing pages, product descriptions, and other content-heavy materials.
I'm sure there's a plugin for that 🤔
Adding a design system
I like to use theme-ui as a base for my design systems. Comes with flexible theming support and a great set of default components.
yarn add theme-ui @theme-ui/presets
Installs theme-ui and default presets with configured themes.
In NextJS, you add a root <ThemeProvider>
in the _app.js
file. Lets you wrap the component tree and gives you access to theming anywhere in your app.
// pages/_app.js
import { ThemeProvider } from "theme-ui"
import theme from "../styles/theme"
function MyApp({ Component, pageProps }) {
return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
)
}
We created a custom theme in styles/theme.js
that extends from the deep
preset. Because deep's colors looked pretty :)
// styles/theme.js
import { deep } from "@theme-ui/presets"
export default {
...deep,
sizes: {
...deep.sizes,
container: 1024,
},
}
Custom container size means we can center our app with <Container> ... </Container>
. Might need to add more sizes to make it responsive.
Yes, theme-ui has support for that 🤘
Creating a form
Forms are notoriously difficult and that's why we used my new favorite form library, react-hook-form. It's great.
// components/FrontmatterForm.js
import { useForm } from "react-hook-form"
import { Label, Input, Textarea, Button } from "theme-ui"
export const FrontmatterForm = ({ onSubmit }) => {
const { register, handleSubmit } = useForm()
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Label>Title</Label>
<Input name="title" ref={register} />
<Label>Description</Label>
<Textarea name="description" ref={register} />
<Button type="submit" bg="secondary">
Submit
</Button>
</form>
)
}
Our form is simple – title and description – and react-hook-form made it even simpler. ref={register}
sets up state management machinery, theme-ui default components make it pretty.
When you hit submit, the onSubmit
callback gets values in a dictionary.
We put them in a useState
inside the main page.
// pages/index.js
export default function Home() {
const [frontmatter, setFrontmatter] = useState(null);
return (
<>
<Container>
<h1>Swiz CMS is where articles are born</h1>
<p>Use this to setup your copypasta for a new article</p>
<FrontmatterForm onSubmit={setFrontmatter} />
<Box sx={{ p: 4 }}>
{frontmatter ? <SocialCardImage {...frontmatter} /> : null}
{frontmatter ? <Frontmatter {...frontmatter} /> : null}
</Box>
</Container>
FrontmatterForm returns data when it's ready, sets state, and re-renders the page. That shows 2 new result components.
Talking to a serverless function on AWS Lambda
We used react-query for talking to a server. An AWS Lambda running my screenshot machinery in this case.
// components/SocialCardImage.js
import { useQuery } from "react-query"
import { Image, Spinner } from "theme-ui"
async function fetchSocialCard(key, title) {
const res = await fetch(
`https://pifc233qp6.execute-api.us-east-1.amazonaws.com/dev/social-card?title=${title}`
)
return res.json()
}
export const SocialCardImage = ({ title }) => {
const cardQuery = useQuery(["social-card", title], fetchSocialCard)
if (cardQuery.isLoading) {
return <Spinner />
}
return <Image src={cardQuery.data.url} />
}
React Query is like Apollo Client, but for REST. It's fantastic.
You give your query a name, define a fetch function that returns data, and library handles the rest. Deals with caching, deduping, refetching, and coordination between different components making the same request.
Proper article on React Query coming soon.
Verdict on NextJS
👍
Cheers,
~Swizec
Continue reading about Your first NextJS app – CodeWithSwiz
Semantically similar articles hand-picked by GPT-4
- Building a small CMS with NextJS, pt2 – CodeWithSwiz
- Why NextJS /api routes are fab – CodeWithSwiz 6
- React Bricks - visual blocks editor for NextJS #CodeWithSwiz 22
- Exploring NextJS with a headless CMS, pt4 – CodeWithSwiz
- Is hot dog taco?
Learned something new?
Read more Software Engineering Lessons from Production
I write articles with real insight into the career and skills of a modern software engineer. "Raw and honest from the heart!" as one reader described them. Fueled by lessons learned over 20 years of building production code for side-projects, small businesses, and hyper growth startups. Both successful and not.
Subscribe below 👇
Software Engineering Lessons from Production
Join Swizec's Newsletter and get insightful emails 💌 on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.
"Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. 👌"
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help 👉 swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers 👉 ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❤️