Getting Started
Choose an option below to see framework specific guides on how to set up Palette.
Install Palette
- npm
- pnpm
- yarn
npm install @palette.dev/browser
pnpm install @palette.dev/browser
yarn add @palette.dev/browser
Initialize Palette
Import palette before all other imports in your app's entrypoint file.
Get your client key at https://palette.dev/[your-username]/[your-project]/settings
- Next.js
- Create React App
- React
- Other
- Next.js >=15.3
- Next.js (App Router)
- Next.js (Pages Router)
import {
init,
events,
markers,
network,
vitals,
profiler,
paint,
tag,
} from "@palette.dev/browser";
init({
key: process.env.NEXT_PUBLIC_PALETTE_CLIENT_KEY!,
plugins: [
events(),
network(),
vitals(),
markers(),
profiler(),
paint({ componentPaint: true }),
],
});
// Start profiler on user interactions
profiler.on(
[
"paint.click",
"paint.keydown",
"paint.scroll",
"paint.mousemove",
"markers.measure",
"events.load",
"events.dcl",
],
{
sampleInterval: 1,
maxBufferSize: 100_000,
}
);
export function onRouterTransitionStart(url: string) {
tag("palette.location", url);
}
import PaletteProvider from "@/components/palette-provider";
export default function RootLayout({ children }: Readonly<{ children: React.ReactNode; }>) {
return (
<html>
<body>
/* Initialize Palette */
<PaletteProvider/>
{children}
</body>
</html>
);
}
"use client";
import { useSearchParams, usePathname } from "next/navigation";
import { useEffect, Suspense } from "react";
import {
init,
events,
markers,
network,
vitals,
profiler,
paint,
tag,
} from "@palette.dev/browser";
const useLocation = () => {
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
tag("palette.location", pathname);
searchParams.forEach((value, key) => {
tag(`router.query.${key}`, value);
});
}, [pathname, searchParams]);
};
function PaletteProviderInner() {
useLocation();
useEffect(() => {
init({
key: process.env.NEXT_PUBLIC_PALETTE_CLIENT_KEY!,
plugins: [
events(),
network(),
vitals(),
markers(),
profiler(),
paint({ componentPaint: true }),
],
});
// Start the profiler on the following events:
profiler.on(
[
"paint.click",
"paint.keydown",
"paint.scroll",
"paint.mousemove",
"markers.measure",
"events.load",
"events.dcl",
],
{
sampleInterval: 1,
maxBufferSize: 100_000,
}
);
}, []);
return null;
}
export default function PaletteProvider() {
return (
<Suspense fallback={null}>
<PaletteProviderInner />
</Suspense>
);
}
import type { AppProps } from "next/app";
import { useRouter } from "next/router";
import { useEffect } from "react";
import {
init,
events,
markers,
network,
vitals,
profiler,
paint,
tag,
} from "@palette.dev/browser";
import "../styles/globals.css";
init({
key: process.env.NEXT_PUBLIC_PALETTE_CLIENT_KEY!,
plugins: [
events(),
network(),
vitals(),
markers(),
profiler(),
paint({ componentPaint: true }),
],
});
// Start the profiler on the following events:
profiler.on(
[
"paint.click",
"paint.keydown",
"paint.scroll",
"paint.mousemove",
"markers.measure",
"events.load",
"events.dcl",
],
{
sampleInterval: 1,
maxBufferSize: 100_000,
}
);
const useLocation = () => {
const { pathname, query } = useRouter();
// Track route query params
useEffect(() => {
tag("palette.location", pathname);
Object.entries(query).forEach(([key, value]) => {
tag(
`router.query.${key}`,
Array.isArray(value) ? value.join(",") : value ?? ""
);
});
}, [pathname, query]);
};
export default function App({ Component, pageProps }: AppProps) {
useLocation();
return <Component {...pageProps} />;
}
// Import palette before all other imports
import { init, events, markers, network, vitals, profiler, paint } from "@palette.dev/browser";
init({
key: "YOUR_CLIENT_KEY",
// Collect click, network, performance events, and profiles
plugins: [events(), network(), vitals(), markers(), profiler(), paint({ componentPaint: true})],
});
// Start the profiler on the following events:
profiler.on(
[
"paint.click",
"paint.keydown",
"paint.scroll",
"paint.mousemove",
"markers.measure",
"events.load",
"events.dcl",
],
{
sampleInterval: 1,
maxBufferSize: 100_000,
}
);
// Your other imports...
// Import palette before all other imports
import { init, events, markers, network, vitals, profiler, paint } from "@palette.dev/browser";
init({
key: "YOUR_CLIENT_KEY",
// Collect click, network, performance events, and profiles
plugins: [events(), network(), vitals(), markers(), profiler(), paint({ componentPaint: true})],
});
// Start the profiler on the following events:
profiler.on(
[
"paint.click",
"paint.keydown",
"paint.scroll",
"paint.mousemove",
"markers.measure",
"events.load",
"events.dcl",
],
{
sampleInterval: 1,
maxBufferSize: 100_000,
}
);
// Your other imports...
// Import palette before all other imports
import { init, events, markers, network, vitals, profiler, paint } from "@palette.dev/browser";
init({
key: "YOUR_CLIENT_KEY",
// Collect click, network, performance events, and profiles
plugins: [events(), network(), vitals(), markers(), profiler(), paint({ componentPaint: true})],
});
// Start the profiler on the following events:
profiler.on(
[
"paint.click",
"paint.keydown",
"paint.scroll",
"paint.mousemove",
"markers.measure",
"events.load",
"events.dcl",
],
{
sampleInterval: 1,
maxBufferSize: 100_000,
}
);
// Your other imports...
Upload source maps
Configure your bundler to upload source maps during build time.
This step is required if you are using a framework or a bundler (like Next.js, Webpack, Vite, or Parcel).
Verify uploads at https://palette.dev/[team]/[project]/settings/sourcemaps
- Next.js
- Create React App (ejected)
- Webpack
- Vite
- Other
- npm
- pnpm
- yarn
npm install @palette.dev/webpack-plugin --save-dev
pnpm install @palette.dev/webpack-plugin --save-dev
yarn add @palette.dev/webpack-plugin --dev
import { withPalette } from "@palette.dev/webpack-plugin/next";
const withPalettePlugin = withPalette({
key: process.env.PALETTE_ASSET_KEY,
});
export default withPalettePlugin({
// your next config...
});
- npm
- pnpm
- yarn
npm install @palette.dev/webpack-plugin --save-dev
pnpm install @palette.dev/webpack-plugin --save-dev
yarn add @palette.dev/webpack-plugin --dev
import PalettePlugin, { PaletteReactPaintPlugin } from "@palette.dev/webpack-plugin";
export default {
// ...
plugins: [
process.env.NODE_ENV === "production" && new PalettePlugin({
key: process.env.PALETTE_ASSET_KEY,
include: ["build/static/js"],
}),
// Enable React component paint performance monitoring
process.env.NODE_ENV === "production" && new PaletteReactPaintPlugin(),
].filter(Boolean),
};
- npm
- pnpm
- yarn
npm install @palette.dev/webpack-plugin --save-dev
pnpm install @palette.dev/webpack-plugin --save-dev
yarn add @palette.dev/webpack-plugin --dev
import PalettePlugin, { PaletteReactPaintPlugin } from "@palette.dev/webpack-plugin";
export default {
// ...
plugins: [
new PalettePlugin({
key: process.env.PALETTE_ASSET_KEY,
}),
// Enable React component paint performance monitoring
new PaletteReactPaintPlugin(),
],
};
- npm
- pnpm
- yarn
npm install @palette.dev/plugin-vite --save-dev
pnpm install @palette.dev/plugin-vite --save-dev
yarn add @palette.dev/plugin-vite --dev
import path from "path";
import { defineConfig } from "vite";
import palette from "@palette.dev/plugin-vite";
export default defineConfig({
plugins: [
// Add palette plugin
palette({
key: process.env.PALETTE_ASSET_KEY,
outputPath: "dist/assets",
}),
],
build: {
// Output source maps
sourcemap: true,
rollupOptions: {
output: {
// Set source maps paths relative to the current working directory
sourcemapPathTransform: (relativeSourcePath, sourcemapPath) =>
path.relative(process.cwd(), path.resolve(path.dirname(sourcemapPath), relativeSourcePath)),
},
},
},
});
- npm
- pnpm
- yarn
npm install @palette.dev/cli --save-dev
pnpm install @palette.dev/cli --save-dev
yarn add @palette.dev/cli --dev
my-project/
├── dist/
│ └── main.js
└── package.json
{
"scripts": {
"upload": "palette upload dist"
}
}
Set asset key
In your CI, set an env variable named PALETTE_ASSET_KEY
to your asset key. This is necessary since source maps are only uploaded in CI.
Get your asset key at https://palette.dev/[your-username]/[your-project]/settings
.
- Vercel
- Netlify
- GitHub Workflows
npx vercel env add
npx netlify env:set PALETTE_ASSET_KEY "YOUR_ASSET_KEY"
gh secret set PALETTE_ASSET_KEY
Add headers
To enable profiling, you need to add the "Document-Policy": "js-profiling"
to your server responses.
- Next.js
- Vercel
- Netlify
You can skip this step, this was handled by the withPalette
util in step 3.
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Document-Policy",
"value": "js-profiling"
}
]
}
]
}
Document-Policy: js-profiling