PB Import bulk-creates Drupal nodes and hierarchical Paragraphs structures from CSV files, built specifically to populate Paragraphs Bundles components like slideshows, accordions and tabs without building each one by hand. The core module declares core_version_requirement: ^11.3 || ^12, and its two submodules match it exactly.
Requirements — and one number the published page gets wrong
The current drupal.org project page still advertises "Drupal 10.0 or higher." That is stale. All three .info.yml files in this project — pb_import, pb_import_node and pb_import_para — declare ^11.3 || ^12, with no Drupal 10 branch at all. Installing on Drupal 10 will fail at the core-version check, not partially work.
| Module | Core | Depends on |
|---|---|---|
| pb_import | ^11.3 || ^12 | Block, Field, File, Filter, Image, Link, Node, Options, System, Text, User, Views, Taxonomy · Entity Reference Revisions · Paragraphs |
| pb_import_node | ^11.3 || ^12 | pb_import |
| pb_import_para | ^11.3 || ^12 | pb_import · Paragraphs Bundles |
Composer's own manifest additionally pulls in drupal/paragraphs_bundles:^2.0@dev for the whole project, ahead of what the base module's own dependency list requires — worth knowing if you are pinning versions in a build.
Three admin screens, three different permissions
PB Import does not gate everything behind one permission. Each screen checks something different, which matters if you are deciding who on your team gets access:
| Screen | Path | Permission required |
|---|---|---|
| Paragraphs List | /admin/content/paragraphs/paragraphs-list | administer site configuration |
| Register Uploaded Files | /admin/content/paragraphs/register-uploaded-files | administer site configuration |
| Import Nodes | /admin/content/paragraphs/pb-import-node | administer site configuration |
| Import Paragraphs | /admin/content/paragraphs/pb-import-para | administer content |
| Edit / delete a paragraph | /admin/content/paragraph/{id}/edit | administer paragraphs |
Registering files uploaded outside Drupal
A file dropped onto the server by SFTP exists on disk but not in Drupal's file table, so nothing can reference it in a CSV import until it is registered. Upload to a folder under sites/default/files/, go to Register Uploaded Files, enter the relative path — gallery/2024, not an absolute path — and the form scans the directory and creates a file entity for each image it finds.
Importing nodes: exact CSV headers
The header row is validated against a fixed list — both missing and extra columns fail validation, so the file has to match exactly, in this order, confirmed directly in CSVProcessorNode.php:
csv_image_url,csv_node_title,csv_image_alt,csv_image_title,csv_node_tag,csv_node_body
Only csv_node_title is strictly required on every row; a row needs an image, body content, or both, and rows with neither are skipped rather than failing the whole import. Multiple taxonomy terms in csv_node_tag are pipe-separated — Featured|New — and get created automatically if they don't already exist in the target vocabulary. This creates a PB Import Node content type on first use, with an image field, an unlimited tags reference field, and a full_html body field.
Importing paragraphs: three-level hierarchy
The paragraph importer builds a nested structure rather than flat rows. Its header, confirmed in CSVProcessorPara.php, is different from the node importer's:
csv_image_url,csv_image_alt,csv_image_title,csv_target_title,csv_target_tag,csv_target_body
One import run creates one parent paragraph (named from the "Parent Title" field on the import form), and each CSV row becomes a section paragraph underneath it holding a target paragraph with the row's image, tags and body. The three levels correspond to Slideshow, Accordion and Tabs bundles from Paragraphs Bundles — you choose which on the import form, and only bundles whose submodule is currently enabled appear as options.
The result is not attached to any node automatically. It has to be referenced deliberately: create or edit a PB Import Para node, and pick the imported parent paragraph in its Referenced Paragraph field — or find it directly at the Paragraphs List screen and attach it from there.
Installation
composer require drupal/pb_import
drush en pb_import -y
drush en pb_import_node pb_import_para -y
drush cr
Enable only the submodules you need — the node importer and the paragraph importer are independent of each other, and neither requires the other to be enabled.
Common questions
Does this replace Migrate for content migrations?
No. It is a much lighter tool aimed at editors doing repeated bulk content and component creation from spreadsheets, not a migration framework with its own plugin system, rollback, or source-plugin ecosystem.
Why did my import fail with a header error?
The header row must match the required list exactly — same columns, same order, same spelling, nothing added and nothing missing. Copy the header line from this page rather than retyping it.
Can I import a paragraph bundle that isn't Slideshow, Accordion or Tabs?
Not with this version. The importer only builds those three hierarchies; other Paragraphs Bundles types are placed by an editor directly on a node.
Does it work on Drupal 10?
No — all three modules in this project declare ^11.3 || ^12. There is no 10.x-compatible release of PB Import.
What happens to file permissions after a CSV import with images?
Files registered through SFTP registration keep whatever permissions they were uploaded with; if images fail to render after import, check that the files directory and its contents are readable by the web server before looking anywhere else.
Need help structuring a real content migration?
PB Import is free and open source. If you are looking at thousands of rows, a source system with its own quirks, or a Drupal 10-to-11/12 upgrade that has to happen before any of this runs, that is the work I take on directly.
See my Drupal services, read about what a Drupal developer does day to day, or get in touch about your project.