Payload uses Webpack 5 to build the Admin panel. It comes with support for many common functionalities such as SCSS and Typescript out of the box, but there are many cases where you may need to add support for additional functionalities.

To extend the Webpack config, add the webpack key to your base Payload config, and provide a function that accepts the default Webpack config as its only argument:


import { buildConfig } from 'payload/config';
export default buildConfig({
admin: {
webpack: (config) => {
// Do something with the config here
return config;

Aliasing server-only modules

A common use case for extending the Payload config is to alias server-only modules, thus preventing them from inclusion into the browser JavaScript bundle.

As the Payload config is used in both server and client contexts, you may find yourself writing code in your Payload config that may be incompatible with browser environments.

Examples of non browser-friendly packages:

  • fs
  • stripe
  • authorizenet
  • nodemailer

You may rely on server-only packages such as the above to perform logic in access control functions, hooks, and other contexts (which is great!) but when you boot up your Payload app and try to view it in the browser, you'll likely run into missing dependency issues or other general incompatibilities.

For example, let's say that you have a Collection called Subscriptions which relies on Stripe:


import { CollectionConfig } from 'payload/types';
import createStripeSubscription from './hooks/createStripeSubscription';
export const Subscription: CollectionConfig = {
slug: 'subscriptions',
hooks: {
beforeChange: [
fields: [
name: 'stripeSubscriptionID',
type: 'text',
required: true,

The collection above features a beforeChange hook that creates a Stripe subscription whenever a Subscription document is created in Payload.

That hook might look something like this:


import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const createStripeSubscription = async ({ data, operation }) => {
if (operation === 'create') {
const dataWithStripeID = {...data};
// use Stripe to create a Stripe subscription
const subscription = await stripe.subscriptions.create({
// Configure the subscription accordingly
// Automatically add the Stripe subscription ID
// to the data that will be saved to this Subscription doc
dataWithStripeID.stripeSubscriptionID = subscription.id;
return dataWithStripeID
return data;
export default createStripeSubscription;

As-is, this collection will prevent your Admin panel from bundling or loading correctly, because Stripe relies on some Node-only packages.

To remedy this issue you can extend the Payload Webpack config to alias your entire createStripeSubscription hook to a separate, empty mock file.

Example in payload.config.js:

import { buildConfig } from 'payload/config';
import path from 'path';
import Subscription from './collections/Subscription';
const createStripeSubscriptionPath = path.resolve(__dirname, 'collections/Subscription/hooks/createStripeSubscription.js');
const mockModulePath = path.resolve(__dirname, 'mocks/emptyObject.js');
export default buildConfig({
collections: [
admin: {
webpack: (config) => ({
resolve: {
alias: {
[createStripeSubscriptionPath]: mockModulePath,

The above code will alias the file at path createStripeSubscriptionPath to a mocked module, which might look like this:


export default {};

Now, when Webpack sees that you're attempting to import your createStripeSubscriptionPath file, it'll disregard that actual file and load your mock file instead. Not only will your Admin panel now bundle successfully, you will have optimized its filesize by removing unnecessary code! And you might have learned something about Webpack, too.

Admin environment vars

By default, env variables are not provided to the Admin panel for security and safety reasons. But, Payload provides you with a way to still provide env vars to your frontend code.

Payload will automatically supply any present env variables that are prefixed with PAYLOAD_PUBLIC_ directly to the Admin panel.

For example, if you've got the following environment variable:


This key will automatically be made available to the Payload bundle and can be referenced in your Admin component code as process.env.PAYLOAD_PUBLIC_STRIPE_PUBLISHABLE_KEY.


Access Control