āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā š nextjs/app/api-reference/config/next-config-js/taint ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
The taint option enables support for experimental React APIs for tainting objects and values. This feature helps prevent sensitive data from being accidentally passed to the client. When enabled, you can use:
experimental_taintObjectReference taint objects references.experimental_taintUniqueValue to taint unique values.Good to know: Activating this flag also enables the React
experimentalchannel forappdirectory.
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
taint: true,
},
}
export default nextConfig
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
taint: true,
},
}
module.exports = nextConfig
Warning: Do not rely on the taint API as your only mechanism to prevent exposing sensitive data to the client. See our security recommendations.
The taint APIs allows you to be defensive, by declaratively and explicitly marking data that is not allowed to pass through the Server-Client boundary. When an object or value, is passed through the Server-Client boundary, React throws an error.
This is helpful for cases where:
It is recommended to model your data and APIs so that sensitive data is not returned to contexts where it is not needed.
experimental_taintUniqueValue parameters reference, for more information.In this case, the getUserDetails function returns data about a given user. We taint the user object reference, so that it cannot cross a Server-Client boundary. For example, assuming UserCard is a Client Component.
import { experimental_taintObjectReference } from 'react'
function getUserDetails(id: string): UserDetails {
const user = await db.queryUserById(id)
experimental_taintObjectReference(
'Do not use the entire user info object. Instead, select only the fields you need.',
user
)
return user
}
import { experimental_taintObjectReference } from 'react'
function getUserDetails(id) {
const user = await db.queryUserById(id)
experimental_taintObjectReference(
'Do not use the entire user info object. Instead, select only the fields you need.',
user
)
return user
}
We can still access individual fields from the tainted userDetails object.
export async function ContactPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
const userDetails = await getUserDetails(id)
return (
<UserCard
firstName={userDetails.firstName}
lastName={userDetails.lastName}
/>
)
}
export async function ContactPage({ params }) {
const { id } = await params
const userDetails = await getUserDetails(id)
return (
<UserCard
firstName={userDetails.firstName}
lastName={userDetails.lastName}
/>
)
}
Now, passing the entire object to the Client Component will throw an error.
export async function ContactPage({
params,
}: {
params: Promise<{ id: string }>
}) {
const userDetails = await getUserDetails(id)
// Throws an error
return <UserCard user={userDetails} />
}
export async function ContactPage({ params }) {
const { id } = await params
const userDetails = await getUserDetails(id)
// Throws an error
return <UserCard user={userDetails} />
}
In this case, we can access the server configuration by awaiting calls to config.getConfigDetails. However, the system configuration contains the SERVICE_API_KEY that we don't want to expose to clients.
We can taint the config.SERVICE_API_KEY value.
import { experimental_taintUniqueValue } from 'react'
function getSystemConfig(): SystemConfig {
const config = await config.getConfigDetails()
experimental_taintUniqueValue(
'Do not pass configuration tokens to the client',
config,
config.SERVICE_API_KEY
)
return config
}
import { experimental_taintUniqueValue } from 'react'
function getSystemConfig() {
const config = await config.getConfigDetails()
experimental_taintUniqueValue(
'Do not pass configuration tokens to the client',
config,
config.SERVICE_API_KEY
)
return config
}
We can still access other properties of the systemConfig object.
export async function Dashboard() {
const systemConfig = await getSystemConfig()
return <ClientDashboard version={systemConfig.SERVICE_API_VERSION} />
}
However, passing SERVICE_API_KEY to ClientDashboard throws an error.
export async function Dashboard() {
const systemConfig = await getSystemConfig()
// Someone makes a mistake in a PR
const version = systemConfig.SERVICE_API_KEY
return <ClientDashboard version={version} />
}
Note that, even though, systemConfig.SERVICE_API_KEY is reassigned to a new variable. Passing it to a Client Component still throws an error.
Whereas, a value derived from a tainted unique value, will be exposed to the client.
export async function Dashboard() {
const systemConfig = await getSystemConfig()
// Someone makes a mistake in a PR
const version = `version::${systemConfig.SERVICE_API_KEY}`
return <ClientDashboard version={version} />
}
A better approach is to remove SERVICE_API_KEY from the data returned by getSystemConfig.
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā