---
title: Webpack
label: Webpack
order: 60
desc: The Payload admin panel uses Webpack 5 and supports many common functionalities such as SCSS and Typescript out of the box to give you more freedom.
keywords: admin, webpack, documentation, Content Management System, cms, headless, javascript, node, react, express
---

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:

`payload.config.ts`
```ts
import { buildConfig } from 'payload/config';

export default buildConfig({
	admin: {
		// highlight-start
		webpack: (config) => {
			// Do something with the config here

			return config;
		}
		// highlight-end
	}
});
```

### 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.

<Banner type="success">
	<strong>Tip:</strong><br/>
	To avoid problems with server code making it to your Webpack bundle, you can use the <strong>alias</strong> Webpack feature to tell Webpack to avoid importing the modules you want to restrict to server-only.
</Banner>

<strong>For example, let's say that you have a Collection called `Subscriptions` which relies on Stripe:</strong>

<br/><br/>

`collections/Subscriptions/index.js`
```ts
import { CollectionConfig } from 'payload/types';
import createStripeSubscription from './hooks/createStripeSubscription';

const Subscription: CollectionConfig = {
	slug: 'subscriptions',
	hooks: {
		beforeChange: [
			createStripeSubscription,
		]
	},
	fields: [
		{
			name: 'stripeSubscriptionID',
			type: 'text',
			required: true,
		}
	]
};

export default Subscription;
```

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

<strong>That hook might look something like this:</strong>

<br/><br/>

`collections/Subscriptions/hooks/createStripeSubscription.js`
```js
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;
```

<Banner type="error">
	<strong>Warning:</strong><br/>
	The above code is NOT production-ready and should not be referenced to create Stripe subscriptions. Although creating a beforeChange hook is a completely valid spot to do things like create subscriptions, the code above is incomplete and insecure, meant for explanation purposes only.
</Banner>

**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`:
```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: [
		Subscription
	],
	admin: {
		webpack: (config) => ({
			...config,
			resolve: {
				...config.resolve,
				alias: {
					...config.resolve.alias,
					[createStripeSubscriptionPath]: mockModulePath,
				}
			}
		})
	}
});
```

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

`mocks/emptyObject.js`
```js
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.

<Banner type="success">
	<strong>Tip:</strong><br/>
	If changes to your Webpack aliases are not surfacing, they might be [cached](https://webpack.js.org/configuration/cache/) in `node_modules/.cache/webpack`. Try deleting that folder and restarting your server.
</Banner>

## Admin environment vars

<Banner type="warning">
	<strong>Important:</strong><br />
	Be careful about what variables you provide to your client-side code. Analyze every single one to make sure that you're not accidentally leaking anything that an attacker could exploit. Only keys that are safe to be available to everyone in plain text should be provided to your Admin panel.
</Banner>

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:

`PAYLOAD_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_XXXXXXXXXXXXXXXXXX`

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`.
