Overview
This is a memo on how to add content types and fields using Drupal custom modules.
The following two articles were helpful.
https://www.drupal.org/docs/drupal-apis/entity-api/creating-a-custom-content-type-in-drupal-8
Car Brand Example
Following the first article introduced above, I was able to add a content type “Car Brand” with a “body” field.

Note that the above article skips the part about creating the custom module. First, create the following folder and file.
name: foobar
description: Sample module
package: Custom
type: module
version: 1.0
core_version_requirement: ^8 || ^9
Adding Custom Fields
Using the above as reference, I was able to add a content type, but to add custom fields, I needed to add both a Field and a Field storage.
When I was unsure about how to write the YAML for Field storage, the second link introduced at the beginning was helpful.
By using the “Configuration Manager” module, I was able to check the definitions of already registered Fields and Field storages.
Based on the above, let’s create a content type called “IIIF Media” and add a field for storing a string called iiif_image_url, and fields for storing numeric values called iiif_image_width and iiif_image_height. The following files are needed.
Content Type
modules/custom/foobar/config/install/node.type.iiif_media.yml
# node.type.iiif_media.yml
langcode: en
status: true
dependencies:
enforced:
module:
- foobar # This is the name of the module we're using for this example
name: 'IIIF Media'
type: iiif_media
description: 'Content type for IIIF Media'
help: ''
new_revision: false
preview_mode: 1
display_submitted: true
Field: iiif_image_url (string)
Field storage
modules/custom/foobar/config/install/field.storage.node.field_iiif_image_url.yml
langcode: en
status: true
dependencies:
enforced:
module:
- foobar
id: node.field_iiif_image_url
field_name: field_iiif_image_url
entity_type: node
type: string
settings:
max_length: 255
case_sensitive: false
is_ascii: false
module: core
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
Note that by setting dependencies > enforced > module > {module name}, the storage is also deleted when the module is uninstalled. Without this setting, only the storage information remained after uninstalling.
The following article was helpful.
https://www.drupal.org/project/drupal/issues/3231028
!
On the other hand, having related content types and fields deleted when uninstalling a module can be problematic in some cases, so care must be taken with these settings.
Field
modules/custom/foobar/config/install/field.field.node.iiif_media.field_iiif_image_url.yml
# field.field.node.iiif_media.field_iiif_image_url.yml
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_iiif_image_url
- node.type.iiif_media
id: node.iiif_media.field_iiif_image_url
field_name: field_iiif_image_url
entity_type: node
bundle: iiif_media
label: IIIF Image URL
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings: { }
field_type: string
Field: iiif_image_width (integer)
Field storage
modules/custom/foobar/config/install/field.storage.node.field_iiif_image_width.yml
langcode: en
status: true
dependencies:
enforced:
module:
- foobar
id: node.field_iiif_image_width
field_name: field_iiif_image_width
entity_type: node
type: integer
settings:
unsigned: false
size: normal
module: core
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false
Field
modules/custom/foobar/config/install/field.field.node.iiif_media.field_iiif_image_width.yml
# field.field.node.iiif_media.field_iiif_image_width.yml
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_iiif_image_width
- node.type.iiif_media
id: node.iiif_media.field_iiif_image_width
field_name: field_iiif_image_width
entity_type: node
bundle: iiif_media
label: IIIF Image Width
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
min: null
max: null
prefix: ''
suffix: ''
field_type: integer
Summary
As a result, by installing the custom module “foobar,” I was able to create a content type called “IIIF Media” and add fields with string and integer field types, as shown below.

I hope this serves as a useful reference for adding content types and fields using custom modules.