Loading...
Loading...
Netlify JAMstack deployment platform with serverless functions, forms, and identity
npx skill4agent add bobmatnyc/claude-mpm-skills netlify-deployment-platform# React (Create React App, Vite)
Build command: npm run build
Publish directory: build (CRA) or dist (Vite)
# Next.js (Static Export)
Build command: npm run build && npm run export
Publish directory: out
# Vue.js
Build command: npm run build
Publish directory: dist
# Gatsby
Build command: gatsby build
Publish directory: public
# Hugo
Build command: hugo
Publish directory: public
# Svelte/SvelteKit
Build command: npm run build
Publish directory: build# Install Netlify CLI
npm install -g netlify-cli
# Login to Netlify
netlify login
# Initialize site
netlify init
# Deploy draft (preview URL)
netlify deploy
# Deploy to production
netlify deploy --prod
# Deploy with build
netlify deploy --build --prod# netlify.toml
[build]
# Build command
command = "npm run build"
# Publish directory
publish = "dist"
# Functions directory
functions = "netlify/functions"
# Production context
[context.production]
command = "npm run build:prod"
[context.production.environment]
NODE_ENV = "production"
API_URL = "https://api.example.com"
# Deploy Preview context
[context.deploy-preview]
command = "npm run build:preview"
# Branch deploys
[context.branch-deploy]
command = "npm run build"[build]
command = "npm run build"
publish = "dist"
# Base directory for monorepos
base = "packages/web"
# Ignore builds on certain changes
ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF packages/web"
[build.environment]
NODE_VERSION = "18"
NPM_VERSION = "9"
RUBY_VERSION = "3.1"[build]
command = "npm run build"
publish = "dist"
# Build processing
[build.processing]
skip_processing = false
[build.processing.css]
bundle = true
minify = true
[build.processing.js]
bundle = true
minify = true
[build.processing.images]
compress = true# Via CLI
netlify env:set API_KEY "secret-value"
netlify env:set NODE_ENV "production"
# List variables
netlify env:list
# Import from .env file
netlify env:import .env# netlify.toml
[context.production.environment]
API_URL = "https://api.production.com"
ENABLE_ANALYTICS = "true"
[context.deploy-preview.environment]
API_URL = "https://api.staging.com"
ENABLE_ANALYTICS = "false"
[context.branch-deploy.environment]
API_URL = "https://api.dev.com"// During build
const apiUrl = process.env.API_URL;
// Client-side (must be prefixed)
const publicKey = process.env.REACT_APP_PUBLIC_KEY;// netlify/functions/hello.js
exports.handler = async (event, context) => {
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'Hello from Netlify Function',
path: event.path,
method: event.httpMethod,
}),
};
};// netlify/functions/api.ts
import { Handler, HandlerEvent, HandlerContext } from '@netlify/functions';
interface RequestBody {
name: string;
email: string;
}
export const handler: Handler = async (
event: HandlerEvent,
context: HandlerContext
) => {
if (event.httpMethod !== 'POST') {
return {
statusCode: 405,
body: 'Method Not Allowed',
};
}
const { name, email }: RequestBody = JSON.parse(event.body || '{}');
return {
statusCode: 200,
body: JSON.stringify({
message: `Hello ${name}`,
email,
}),
};
};// netlify/functions/database.js
const { MongoClient } = require('mongodb');
let cachedDb = null;
async function connectToDatabase() {
if (cachedDb) return cachedDb;
const client = await MongoClient.connect(process.env.MONGODB_URI);
cachedDb = client.db();
return cachedDb;
}
exports.handler = async (event) => {
const db = await connectToDatabase();
const users = await db.collection('users').find({}).toArray();
return {
statusCode: 200,
body: JSON.stringify(users),
};
};// netlify/functions/scheduled.js
const { schedule } = require('@netlify/functions');
const handler = async () => {
console.log('Running scheduled task');
// Your scheduled logic
await performBackup();
return {
statusCode: 200,
};
};
// Run every day at midnight
exports.handler = schedule('0 0 * * *', handler);// netlify/functions/background-task.js
// Auto-runs in background if response is 200 within 10s
exports.handler = async (event) => {
// Long-running task
await processLargeDataset();
return {
statusCode: 200,
};
};
// Invoke: POST to /.netlify/functions/background-task<!-- Contact form -->
<form name="contact" method="POST" data-netlify="true">
<input type="hidden" name="form-name" value="contact" />
<label>Name: <input type="text" name="name" required /></label>
<label>Email: <input type="email" name="email" required /></label>
<label>Message: <textarea name="message" required></textarea></label>
<button type="submit">Send</button>
</form>// ContactForm.jsx
import { useState } from 'react';
export default function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: '',
});
const handleSubmit = async (e) => {
e.preventDefault();
const response = await fetch('/', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
'form-name': 'contact',
...formData,
}).toString(),
});
if (response.ok) {
alert('Thank you for your message!');
}
};
return (
<form name="contact" onSubmit={handleSubmit} data-netlify="true">
<input type="hidden" name="form-name" value="contact" />
{/* Form fields */}
</form>
);
}<!-- Spam filtering with honeypot -->
<form name="contact" method="POST" data-netlify="true" data-netlify-honeypot="bot-field">
<input type="hidden" name="form-name" value="contact" />
<p class="hidden">
<label>Don't fill this out: <input name="bot-field" /></label>
</p>
<!-- Form fields -->
</form>
<!-- reCAPTCHA v2 -->
<form name="contact" method="POST" data-netlify="true" data-netlify-recaptcha="true">
<div data-netlify-recaptcha="true"></div>
<button type="submit">Submit</button>
</form>
<!-- File uploads -->
<form name="file-upload" method="POST" data-netlify="true" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">Upload</button>
</form># netlify.toml
[[plugins]]
package = "@netlify/plugin-emails"
[plugins.inputs]
formName = "contact"
to = "admin@example.com"
subject = "New contact form submission"// Add to HTML
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
// Initialize
if (window.netlifyIdentity) {
window.netlifyIdentity.on('init', user => {
if (!user) {
window.netlifyIdentity.on('login', () => {
document.location.href = '/admin/';
});
}
});
}// useNetlifyIdentity.js
import { useEffect, useState } from 'react';
export function useNetlifyIdentity() {
const [user, setUser] = useState(null);
useEffect(() => {
const netlifyIdentity = window.netlifyIdentity;
netlifyIdentity.on('init', user => setUser(user));
netlifyIdentity.on('login', user => setUser(user));
netlifyIdentity.on('logout', () => setUser(null));
netlifyIdentity.init();
}, []);
return {
user,
login: () => window.netlifyIdentity.open('login'),
logout: () => window.netlifyIdentity.logout(),
signup: () => window.netlifyIdentity.open('signup'),
};
}// netlify/functions/protected.js
exports.handler = async (event, context) => {
const { user } = context.clientContext;
if (!user) {
return {
statusCode: 401,
body: 'Unauthorized',
};
}
return {
statusCode: 200,
body: JSON.stringify({
message: 'Protected data',
user: user.email,
}),
};
};# _redirects file in publish directory
# Redirect with status code
/old-path /new-path 301
# Rewrite (proxy)
/api/* https://api.example.com/:splat 200
# SPA fallback
/* /index.html 200
# Force HTTPS
http://example.com/* https://example.com/:splat 301!
# Conditional redirects
/news/* /blog/:splat 302 Country=us
# Role-based redirects
/admin/* /admin/dashboard 200! Role=admin
/admin/* /unauthorized 401[[redirects]]
from = "/old-path"
to = "/new-path"
status = 301
[[redirects]]
from = "/api/*"
to = "https://api.example.com/:splat"
status = 200
force = true
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
[[redirects]]
from = "/admin/*"
to = "/admin/dashboard"
status = 200
conditions = {Role = ["admin"]}
# Proxy with headers
[[redirects]]
from = "/proxy/*"
to = "https://backend.com/:splat"
status = 200
force = true
[redirects.headers]
X-From = "Netlify"[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
Content-Security-Policy = "default-src 'self'"
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"# netlify.toml
[context.deploy-preview]
command = "npm run build:preview"
[context.deploy-preview.environment]
NODE_ENV = "preview"
API_URL = "https://api-staging.example.com"# Deploy specific branches
[context.staging]
command = "npm run build:staging"
[context.staging.environment]
API_URL = "https://api-staging.example.com"
# Branch pattern matching
[context.branch-deploy]
command = "npm run build"# GitHub PR comments
# Slack notifications
# Email notifications
# Configured in Netlify UI# netlify.toml
[[redirects]]
from = "/*"
to = "/version-a/:splat"
status = 200
conditions = {Cookie = ["ab_test=a"]}
force = true
[[redirects]]
from = "/*"
to = "/version-b/:splat"
status = 200
conditions = {Cookie = ["ab_test=b"]}
force = true
# 50/50 split
[[redirects]]
from = "/*"
to = "/version-a/:splat"
status = 200!
percentage = 50
[[redirects]]
from = "/*"
to = "/version-b/:splat"
status = 200!// netlify/edge-functions/hello.ts
import type { Context } from "https://edge.netlify.com";
export default async (request: Request, context: Context) => {
const url = new URL(request.url);
return new Response(`Hello from ${url.pathname}`, {
headers: { "content-type": "text/html" },
});
};
export const config = { path: "/hello" };// netlify/edge-functions/geo.ts
import type { Context } from "https://edge.netlify.com";
export default async (request: Request, context: Context) => {
const { city, country } = context.geo;
return Response.json({
location: `${city}, ${country}`,
});
};// netlify/edge-functions/transform.ts
import type { Context } from "https://edge.netlify.com";
export default async (request: Request, context: Context) => {
const response = await context.next();
const text = await response.text();
// Modify HTML
const modified = text.replace(
'</body>',
'<script>console.log("Injected by edge");</script></body>'
);
return new Response(modified, response);
};
export const config = { path: "/*" };# Via CLI
netlify domains:add example.com
# DNS Configuration
# A record: 75.2.60.5
# CNAME: <site-name>.netlify.app
# Verify domain
netlify domains:verify example.com# Automatic HTTPS (default)
# Free Let's Encrypt certificates
# Auto-renewal
# Force HTTPS redirect
[[redirects]]
from = "http://example.com/*"
to = "https://example.com/:splat"
status = 301
force = true<!-- Automatically injected, no code needed -->
<!-- Server-side analytics, no client-side JS -->// Track custom events
function trackEvent(eventName, data) {
fetch('/.netlify/functions/analytics', {
method: 'POST',
body: JSON.stringify({ event: eventName, ...data }),
});
}
trackEvent('button_click', { button: 'signup' });# Run functions locally
netlify dev
# Specific port
netlify dev --port 3000
# Live session sharing
netlify dev --live
# Functions only
netlify functions:serve# Link existing site
netlify link
# Create new site
netlify sites:create
# List sites
netlify sites:list
# Site info
netlify status
# Open site in browser
netlify open# List deploys
netlify deploy:list
# Rollback to previous deploy
netlify rollback
# Cancel deploy
netlify deploy:cancel <deploy-id># netlify.toml
[build]
command = "npm run build"
publish = "dist"
# Auto-publish on Git push
# Production: main/master branch
# Previews: all pull requests
# Branch deploys: configured branches# Trigger builds via webhook
curl -X POST -d {} https://api.netlify.com/build_hooks/<hook-id>
# Scheduled builds (use external cron + webhook)# Enable processing
[build.processing]
skip_processing = false
[build.processing.css]
bundle = true
minify = true
[build.processing.js]
bundle = true
minify = true
[build.processing.images]
compress = true
# Asset optimization
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
X-XSS-Protection = "1; mode=block"
Referrer-Policy = "strict-origin-when-cross-origin"
Permissions-Policy = "geolocation=(), microphone=(), camera=()"
Content-Security-Policy = """
default-src 'self';
script-src 'self' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' data:;
"""// Keep functions lightweight
// Use connection pooling
// Cache external API responses
// Set appropriate timeouts
// Handle errors gracefully
exports.handler = async (event) => {
try {
// Set timeout
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 8000);
const response = await fetch(API_URL, {
signal: controller.signal,
});
clearTimeout(timeout);
return {
statusCode: 200,
body: JSON.stringify(await response.json()),
};
} catch (error) {
console.error('Function error:', error);
return {
statusCode: 500,
body: JSON.stringify({ error: 'Internal server error' }),
};
}
};[build]
command = "npm run build"
publish = "dist"
# Skip builds when not needed
ignore = """
git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF -- . ':(exclude)docs/' ':(exclude)*.md'
"""
# Cache dependencies
[build.environment]
NPM_FLAGS = "--legacy-peer-deps"
NODE_OPTIONS = "--max-old-space-size=4096"# netlify.toml
[build]
base = "packages/web"
command = "npm run build"
publish = "dist"
# Only build when package changes
ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF packages/web"[build]
command = "npm run build"
publish = "build"
functions = "netlify/functions"
[[redirects]]
from = "/api/*"
to = "/.netlify/functions/:splat"
status = 200
[[redirects]]
from = "/*"
to = "/index.html"
status = 200[[redirects]]
from = "/blog/*"
to = "https://blog.example.com/:splat"
status = 200
force = true[[redirects]]
from = "/app/*"
to = "/app/dashboard"
status = 200
conditions = {Role = ["user"]}
[[redirects]]
from = "/app/*"
to = "/login"
status = 302