After setting up countless Next.js projects, I've refined my configuration to maximize type safety and code consistency. Let me walk you through my setup and explain why each option matters.
#TypeScript Configuration That Makes Sense
First, let's look at the tsconfig.json
that I use in every project:
#Compilation and Environment Settings
{
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "esnext",
"moduleResolution": "node"
}
This ensures our code compiles to ES5 for maximum browser compatibility while still allowing us to use modern JavaScript features. The lib
array gives us access to DOM types and modern JavaScript APIs.
#Type Safety Settings
{
"strict": false,
"strictNullChecks": true,
"noUncheckedIndexedAccess": true
}
Here's where it gets interesting. While I don't enable all strict checks, I specifically want strictNullChecks
and noUncheckedIndexedAccess
. Let me show you why:
// Without noUncheckedIndexedAccess
const users = ['Alice', 'Bob']
const lastUser = users[999] // TypeScript thinks this is string
// With noUncheckedIndexedAccess
const lastUser = users[999] // TypeScript correctly flags this as string | undefined
This simple setting has saved me from countless runtime errors.
#Project Structure
{
"baseUrl": ".",
"resolveJsonModule": true,
"jsx": "preserve"
}
These settings improve the development experience. baseUrl
enables absolute imports from the project root, while resolveJsonModule
lets us import JSON files directly.
#Prettier Configuration: Beyond Basic Formatting
My .prettierrc
configuration is equally important. Here's why certain settings matter:
#Core Formatting Rules
{
"singleQuote": true,
"semi": false,
"useTabs": true,
"tabWidth": 2,
"printWidth": 80
}
I prefer single quotes and no semicolons for cleaner code. Tabs over spaces because they're more accessible and allow developers to set their preferred indentation width.
#Tailwind Integration
{
"plugins": ["prettier-plugin-tailwindcss"],
"tailwindAttributes": [
"class",
"className",
"ngClass",
".*[cC]lassName"
],
"tailwindFunctions": ["clsx", "cn"]
}
This is crucial if you're using Tailwind CSS. The plugin automatically sorts Tailwind classes in a consistent order. I've added support for various class-related attributes and utility functions like clsx
and cn
.
#File-Specific Overrides
{
"overrides": [
{
"files": ["**/package.json"],
"options": {
"useTabs": false
}
},
{
"files": ["**/*.mdx"],
"options": {
"printWidth": 65,
"proseWrap": "preserve"
}
}
]
}
Different files need different formatting. JSON files work better with spaces, and MDX files need special handling for better readability.
#Why This Combination Works
The magic happens when these configurations work together:
- Type Safety: TypeScript catches potential errors before they hit production.
- Consistent Styling: Prettier ensures the codebase looks uniform regardless of who wrote it.
- Modern Development: We get modern features while maintaining compatibility.
- Team Friendly: These settings work well for teams, reducing conflicts and making code reviews easier.
Here's a real-world example showing how these configurations work together:
// This code is automatically formatted and type-checked
interface User {
name: string
posts?: string[]
}
function getLatestPost(user: User) {
// TypeScript warns us about possible undefined
const post = user.posts?.[0]
return (
<div className="flex flex-col gap-4 p-4 hover:bg-gray-100">
{post && <p>{post}</p>}
</div>
)
}
The TypeScript configuration ensures we handle nullable values correctly, while Prettier formats the code and sorts Tailwind classes automatically.
#Getting Started
To use these configurations in your project:
- Copy the
tsconfig.json
content into your project - Create a
.prettierrc
file with the provided configuration - Install required dependencies:
npm install -D prettier prettier-plugin-tailwindcss
That's it! Your Next.js project now has a solid foundation for type-safe, consistently formatted code.