Project Initialization - Tectonica-Campaigns-Solutions/Tectonica-Dev-Team-Standards-Practices GitHub Wiki
This guide provides step-by-step instructions for initializing new Jamstack projects using our standard tech stack. Following this guide ensures all projects start with consistent structure, configurations, and best practices.
- Before You Begin
- Project Creation
- Initial Configuration
- DatoCMS Project Setup
- Git Repository Setup
- Deployment Platform Setup
- Essential Integrations
- Project Checklist
- Completed Development Environment Setup
- Access to team's GitHub organization
- DatoCMS account with appropriate permissions
- Netlify/Vercel team access
- Project requirements document
Gather the following before starting:
- Project name and slug (URL-friendly version)
- Target deployment platform (Netlify or Vercel)
- Required integrations (Analytics, CMS environments, etc.)
- Domain name (if available)
# Create new Next.js project with TypeScript
npx create-next-app@latest project-name --typescript --tailwind --app
# Navigate to project
cd project-name
When prompted, use these settings:
- ✅ Would you like to use TypeScript? Yes
- ✅ Would you like to use ESLint? Yes
- ✅ Would you like to use Tailwind CSS? Yes
- ✅ Would you like to use
src/
directory? Yes - ✅ Would you like to use App Router? Yes
- ✅ Would you like to customize the default import alias? No
# Core dependencies
npm install @datocms/client graphql graphql-request @datocms/react-ui
# Development dependencies
npm install -D @types/node prettier eslint-config-prettier husky lint-staged
# Optional but recommended
npm install axios react-hook-form zod @vercel/analytics
Create the standard folder structure:
# Create standard directories
mkdir -p src/{components,lib,hooks,utils,styles,types}
mkdir -p src/components/{common,layout,ui}
mkdir -p public/{images,fonts}
mkdir -p scripts
mkdir -p .github/workflows
# Create essential files
touch src/lib/datocms.ts
touch src/types/dato.ts
touch .env.example
touch .env.local
Update tsconfig.json
:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./src/components/*"],
"@/lib/*": ["./src/lib/*"],
"@/hooks/*": ["./src/hooks/*"],
"@/utils/*": ["./src/utils/*"],
"@/types/*": ["./src/types/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
Create .eslintrc.json
:
{
"extends": [
"next/core-web-vitals",
"prettier"
],
"rules": {
"react/no-unescaped-entities": "off",
"@next/next/no-img-element": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
Create .prettierrc
:
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"arrowParens": "always",
"endOfLine": "lf"
}
Create .env.example
:
# DatoCMS
NEXT_PUBLIC_DATOCMS_API_TOKEN=
DATOCMS_API_TOKEN=
DATOCMS_PREVIEW_SECRET=
NEXT_PUBLIC_DATOCMS_ENVIRONMENT=main
# Site Configuration
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_SITE_NAME="Project Name"
# Analytics (optional)
NEXT_PUBLIC_GTM_ID=
NEXT_PUBLIC_GA_ID=
# Deployment
NETLIFY_SITE_ID=
VERCEL_PROJECT_ID=
Update .gitignore
:
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
.env
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# editor
.vscode/*
!.vscode/extensions.json
.idea
- Log into DatoCMS
- Create new project or clone from template
- Configure project settings:
- Project name
- Timezone
- Default locale
In DatoCMS Settings → API Tokens:
-
Read-only Token (for production):
- Name: "Production Read-only"
- Role: Read-only
- Environments: Main
-
Preview Token (for development):
- Name: "Preview/Development"
- Role: Read-only
- Environments: All
-
Full-access Token (for migrations):
- Name: "Development Full Access"
- Role: Admin
- Environments: All
Create src/lib/datocms.ts
:
import { GraphQLClient } from 'graphql-request';
import { cache } from 'react';
export const client = new GraphQLClient('https://graphql.datocms.com/', {
headers: {
authorization: `Bearer ${process.env.DATOCMS_API_TOKEN}`,
'X-Environment': process.env.NEXT_PUBLIC_DATOCMS_ENVIRONMENT || 'main',
},
});
// Cache function for Next.js App Router
export const performRequest = cache(async (query: string, variables = {}) => {
return client.request(query, variables);
});
// Preview client for draft content
export const previewClient = new GraphQLClient('https://graphql.datocms.com/', {
headers: {
authorization: `Bearer ${process.env.DATOCMS_API_TOKEN}`,
'X-Environment': process.env.NEXT_PUBLIC_DATOCMS_ENVIRONMENT || 'main',
'X-Include-Drafts': 'true',
},
});
# Initialize git
git init
# Create initial commit
git add .
git commit -m "Initial commit: Next.js project setup"
# Install husky
npx husky-init && npm install
# Add pre-commit hook
npx husky add .husky/pre-commit "npm run lint-staged"
Add to package.json
:
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,mdx,css,scss}": [
"prettier --write"
]
}
}
# Add remote origin
git remote add origin [email protected]:your-org/project-name.git
# Push to main branch
git push -u origin main
# Create and switch to develop branch
git checkout -b develop
git push -u origin develop
In GitHub repository settings:
- Go to Settings → Branches
- Add rule for
main
branch:- ✅ Require pull request reviews
- ✅ Dismiss stale pull request approvals
- ✅ Require status checks to pass
- ✅ Require branches to be up to date
- ✅ Include administrators
-
Connect Repository:
- Log into Netlify
- New site from Git
- Connect GitHub repository
-
Build Settings:
Build command: npm run build Publish directory: .next
-
Environment Variables:
- Add all variables from
.env.example
- Set production values
- Add all variables from
-
Deploy Contexts:
- Production:
main
branch - Preview:
develop
branch - Deploy previews: Pull requests
- Production:
-
Import Project:
- Log into Vercel
- Import Git repository
- Select team/scope
-
Framework Preset:
- Select Next.js
- Root directory:
./
-
Environment Variables:
- Add all production variables
- Configure preview variables
-
Domain Settings:
- Add custom domain
- Configure www redirect
Google Tag Manager:
// src/app/layout.tsx
import Script from 'next/script';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<head>
{process.env.NEXT_PUBLIC_GTM_ID && (
<Script
id="gtm-script"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','${process.env.NEXT_PUBLIC_GTM_ID}');
`,
}}
/>
)}
</head>
<body>{children}</body>
</html>
);
}
Consider setting up:
- Sentry for error tracking
- LogRocket for session replay
- Datadog for performance monitoring
Create src/app/api/preview/route.ts
:
import { NextRequest, NextResponse } from 'next/server';
import { draftMode } from 'next/headers';
import { redirect } from 'next/navigation';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const secret = searchParams.get('secret');
const slug = searchParams.get('slug');
// Validate secret
if (secret !== process.env.DATOCMS_PREVIEW_SECRET) {
return NextResponse.json({ message: 'Invalid token' }, { status: 401 });
}
// Enable draft mode
draftMode().enable();
// Redirect to the path from the fetched post
redirect(slug || '/');
}
Before considering the project initialized:
- Git repository created and pushed
- Branch protection rules configured
- README.md updated with project info
- Team members added as collaborators
- TypeScript configured
- ESLint and Prettier setup
- Husky pre-commit hooks working
- Environment variables documented
- Project created in DatoCMS
- API tokens generated and stored
- Client library configured
- Preview mode working
- Connected to Netlify/Vercel
- Environment variables configured
- Custom domain setup (if available)
- Deploy previews enabled
- Initial build passes
- No TypeScript errors
- ESLint passing
- Sample page fetching DatoCMS content
- Project README updated
- Environment variables documented
- Setup instructions included
- Team notified of new project
After initialization:
- Review Project Structure Standards
- Set up DatoCMS Content Models
- Implement Component Architecture
- Configure Testing Strategy
June 9, 2025 - Initial guide created