Like what we’re doing? Star us on GitHub!

User CSV Import for a Collection

3 months ago
6 1

What is the best way to go about adding a CSV import to a Collection for Users?

  • DanRibbens
    Payload Team
    2 months ago

    I can think of two good ways to get this done. I typically write an import script that starts up Payload with local: true, reads the CSV file and loops over the rows, mapping data to a data object and calls save on your collection(s). The other option is more work, but you can create a custom endpoint that handles the import from an uploaded file along with adding a custom component in Payload to the UI for users to submit data to.

    Here is a limited example of what I've used for an import script:

    import dotenv from 'dotenv';
    import path from 'path';
    import csv from 'csvtojson';
    import payload from 'payload';
    import { User } from '../payload-types';
    import { UserSource } from './types'; // I like to make types as all strings of my CSV data so that I know I have the field names correct, especially from legacy systems with poor naming conventions.
      secret: process.env.PAYLOAD_SECRET,
      mongoURL: process.env.MONGO_URL,
      local: true,
    (async () => {
      const usersSource: { [key: string]: UserSource } = {};
      const csvPath = path.resolve(__dirname, 'source/users.csv');
        .then(async (fromCSV) => {
          fromCSV.forEach((source: UserSource) => {
            usersSource[] = source;
          // eslint-disable-next-line no-restricted-syntax
          for (const key of Object.keys(usersSource)) {
            const source = usersSource[key];
            const data: Omit<User, 'id' | 'createdAt' | 'updatedAt'> = {
              id: source.ID,
              email: source.EMAIL,
              name: source.NAME,
              // other fields you might have
            try {
              // eslint-disable-next-line no-await-in-loop
              await payload.create({
                collection: 'users',
                overrideAccess: true,
              console.log('created: ',;
            } catch (e) {
              // eslint-disable-next-line no-continue
              console.log('skip: ',;
              // eslint-disable-next-line no-continue
        .then(() => process.exit(0));

    This is a ts file, which you can run with npx ts-node -T imports/users.ts modifying the path for the directory you want.

    These tend to get longer and more complex if you need to also save relationships, but it all be handled this way as well.

    For users specfically, if you're using the local authentication (default) auth, you'll want to have a workflow for users to reset their passwords with a token for first time on the new system.

    Hopefully this gives you what you need. If you need any help we do offer enterprise support agreements that cover this kind of work.

Open the post
Continue the discussion in GitHub
Can't find what you're looking for?
Get help straight from the Payload team with an Enterprise License.Learn More