How to Add Custom Field to WordPress Media: Rocon Quick Guide
August 18, 2025 Written by rocon_rocon
WordPress Troubleshooting
August 18, 2025 Written by rocon_rocon
Add custom field to WordPress media: The default WordPress Media Library lets you add a Title, Caption, Alt Text, and Description. That’s plenty for a lot of webpages. But if you run a brand library, a news site, an e-commerce catalog, or a portfolio, you’ll rapidly want unique fields.
These could include the photographer, copyright holder, license type, product SKU, external source URL, model release status, shoot date, or credit line. This guide shows you how to add such fields in a method that works, save them safely, present them on the front end, and keep everything running smoothly and easy to manage.
Every upload becomes a post with post_type = attachment. The actual file lives in wp-content/uploads, while the attachment post holds metadata (dimensions, mime type, etc.) plus post meta—which is where your custom fields live. You’ll interact with:
Those two are the backbone of most solutions you’ll build below.
Often, you’ll combine them: store scalar values (e.g., “Credit: John Doe”) as meta; use a taxonomy for categories like license type.
Below is a complete, drop-in snippet for your theme’s functions.php (or a small must-use/plugin file). It adds a single text field (“Photographer Name”), shows it in the Media edit screen and modal, saves it safely, and makes it easy to output on the front end.
/**
* Add a custom field to the Media edit screen (and media modal “Attachment Details”).
*/
function mymedia_add_custom_field( $form_fields, $post ) {
$value = get_post_meta( $post->ID, ‘_mymedia_photographer’, true );
// Add a nonce field once per screen load (safe to repeat—WP will de-duplicate by name).
$form_fields[‘mymedia_nonce’] = array(
‘label’ => ”,
‘input’ => ‘html’,
‘html’ => wp_nonce_field( ‘mymedia_save_field_’ . $post->ID, ‘mymedia_nonce’, true, false ),
);
$form_fields[‘mymedia_photographer’] = array(
‘label’ => __( ‘Photographer’, ‘your-textdomain’ ),
‘input’ => ‘text’,
‘value’ => $value,
‘helps’ => __( ‘Enter the photographer or credit line.’, ‘your-textdomain’ ),
);
return $form_fields;
}
add_filter( ‘attachment_fields_to_edit’, ‘mymedia_add_custom_field’, 10, 2 );
/**
* Save our custom field from Media edit screen.
*/
function mymedia_save_custom_field( $post, $attachment ) {
$attachment_id = isset( $post[‘ID’] ) ? (int) $post[‘ID’] : 0;
if ( ! $attachment_id || ! current_user_can( ‘edit_post’, $attachment_id ) ) {
return $post; // Respect capabilities
}
// Nonce check
if ( empty( $_POST[‘mymedia_nonce’] ) || ! wp_verify_nonce( $_POST[‘mymedia_nonce’], ‘mymedia_save_field_’ . $attachment_id ) ) {
return $post;
}
if ( isset( $attachment[‘mymedia_photographer’] ) ) {
$value = sanitize_text_field( $attachment[‘mymedia_photographer’] );
update_post_meta( $attachment_id, ‘_mymedia_photographer’, $value );
}
return $post;
}
add_filter( ‘attachment_fields_to_save’, ‘mymedia_save_custom_field’, 10, 2 );
/**
* Output a credit below a featured image or any attachment.
*/
function mymedia_output_credit( $attachment_id ) {
$credit = get_post_meta( $attachment_id, ‘_mymedia_photographer’, true );
if ( $credit ) {
echo ‘<p class=”photo-credit”>Photo: ‘ . esc_html( $credit ) . ‘</p>’;
}
}
Use mymedia_output_credit( get_post_thumbnail_id() ); in templates, or call it when rendering galleries.
You can filter the markup generated by wp_get_attachment_image() attributes:
add_filter( ‘wp_get_attachment_image_attributes’, function( $attr, $attachment ) {
$credit = get_post_meta( $attachment->ID, ‘_mymedia_photographer’, true );
if ( $credit ) {
$attr[‘data-photo-credit’] = $credit; // handy for JS or tooltips
}
return $attr;
}, 10, 2 );
This helps editors see/use the field without clicking into each item.
// Register column
add_filter( ‘manage_upload_columns’, function( $cols ) {
$cols[‘mymedia_photographer’] = __( ‘Photographer’, ‘your-textdomain’ );
return $cols;
} );
// Render column
add_action( ‘manage_media_custom_column’, function( $column_name, $attachment_id ) {
if ( ‘mymedia_photographer’ === $column_name ) {
$val = get_post_meta( $attachment_id, ‘_mymedia_photographer’, true );
echo $val ? esc_html( $val ) : ‘—’;
}
}, 10, 2 );
You can also make it sortable with manage_upload_sortable_columns and a pre_get_posts modification, if desired.
If your team prefers a polished UI with many field types:
ACF will inject fields on the Media edit screen (and the media modal’s “Attachment Details”).
Retrieving values:
$credit = get_field( ‘photographer’, $attachment_id );
$license_url = get_field( ‘license_url’, $attachment_id );
Show in REST: In ACF’s field group settings (or via code), enable show_in_rest or use ACF’s REST integration so headless/front-end apps (or Gutenberg blocks) can access these values.
Pros: Editor-friendly, lots of field types, validation, conditional logic.
Cons: Adds a plugin dependency; minor performance overhead compared to bare PHP.
Some metadata fits better as categories/tags. Example: License Type (Editorial, Commercial, Creative Commons), or Photographer as a reusable term.
/**
* Register a “media_license” taxonomy for attachments.
*/
function mymedia_register_media_taxonomy() {
register_taxonomy(
‘media_license’,
‘attachment’,
array(
‘label’ => __( ‘Media License’, ‘your-textdomain’ ),
‘public’ => false,
‘show_ui’ => true,
‘show_admin_column’ => true,
‘hierarchical’ => false,
‘show_in_rest’ => true, // expose to REST/Gutenberg
)
);
}
add_action( ‘init’, ‘mymedia_register_media_taxonomy’ );
Editors can then tag images with terms like “Editorial Use Only.” You can query by taxonomy, create filters, and present license badges on the front end.
If you’re not using ACF, register your meta with register_meta() so it’s available via REST:
/**
* Register attachment meta for REST access.
*/
function mymedia_register_rest_meta() {
register_meta( ‘post’, ‘_mymedia_photographer’, array(
‘object_subtype’ => ‘attachment’,
‘show_in_rest’ => array(
‘schema’ => array(
‘type’ => ‘string’,
‘description’ => ‘Photographer credit’,
),
),
‘single’ => true,
‘type’ => ‘string’,
‘auth_callback’ => function() { return current_user_can( ‘upload_files’ ); },
) );
}
add_action( ‘init’, ‘mymedia_register_rest_meta’ );
With this, GET /wp-json/wp/v2/media/<id> includes your field, and editors (with permission) can update it via POST/PATCH.
You have a few options:
A lightweight example of a dynamic block render callback:
function mymedia_block_render( $attributes, $content, $block ) {
if ( empty( $attributes[‘attachmentId’] ) ) {
return $content;
}
$credit = get_post_meta( (int) $attributes[‘attachmentId’], ‘_mymedia_photographer’, true );
if ( ! $credit ) {
return $content;
}
return $content . ‘<figcaption class=”photo-credit”>Photo: ‘ . esc_html( $credit ) . ‘</figcaption>’;
}
Shortcodes are simple for editors who don’t touch templates:
/**
* [photo_credit id=”123″]
*/
function mymedia_shortcode_credit( $atts ) {
$atts = shortcode_atts( array( ‘id’ => 0 ), $atts );
$credit = get_post_meta( (int) $atts[‘id’], ‘_mymedia_photographer’, true );
return $credit ? ‘<span class=”photo-credit”>Photo: ‘ . esc_html( $credit ) . ‘</span>’ : ”;
}
add_shortcode( ‘photo_credit’, ‘mymedia_shortcode_credit’ );
For taxonomy-based fields, WordPress auto-adds the dropdown filter in Media > Library (list view). For pure meta, you can add a custom filter UI and modify pre_get_posts to query by meta key/value.
If you have a CSV mapping attachment_id,photographer, you can loop through it with WP-CLI:
# Example: update all images missing a photographer to “Unknown”
wp eval ‘
$ids = get_posts([“post_type”=>”attachment”,”posts_per_page”=>-1,”fields”=>”ids”]);
foreach ($ids as $id) {
$v = get_post_meta($id, “_mymedia_photographer”, true);
if (!$v) { update_post_meta($id, “_mymedia_photographer”, “Unknown”); }
}
echo “Done\n”;
‘
You can seed your custom field from existing data:
add_action( ‘admin_init’, function() {
if ( ! current_user_can( ‘manage_options’ ) ) return;
if ( isset( $_GET[‘migrate_credit_once’] ) ) {
$ids = get_posts( array( ‘post_type’ => ‘attachment’, ‘posts_per_page’ => -1, ‘fields’ => ‘ids’ ) );
foreach ( $ids as $id ) {
$existing = get_post_meta( $id, ‘_mymedia_photographer’, true );
if ( $existing ) continue;
$alt = get_post_meta( $id, ‘_wp_attachment_image_alt’, true );
if ( $alt ) update_post_meta( $id, ‘_mymedia_photographer’, sanitize_text_field( $alt ) );
}
wp_die( ‘Migration complete.’ );
}
} );
Run once at /wp-admin/?migrate_credit_once=1 (then remove the code).
Accessibility: Credits should not replace proper alt attributes; they complement them.
If you’d prefer a tiny plugin instead of mixing into your theme, create wp-content/plugins/mymedia-credit/mymedia-credit.php:
<?php
/**
* Plugin Name: MyMedia Credit Field
* Description: Adds a Photographer credit field to attachments.
* Version: 1.0.0
* Author: You
*/
if ( ! defined( ‘ABSPATH’ ) ) exit;
add_filter( ‘attachment_fields_to_edit’, function( $form_fields, $post ) {
$val = get_post_meta( $post->ID, ‘_mymedia_photographer’, true );
$form_fields[‘mymedia_nonce’] = array(
‘label’ => ”,
‘input’ => ‘html’,
‘html’ => wp_nonce_field( ‘mymedia_save_field_’ . $post->ID, ‘mymedia_nonce’, true, false ),
);
$form_fields[‘mymedia_photographer’] = array(
‘label’ => __( ‘Photographer’, ‘mymedia’ ),
‘input’ => ‘text’,
‘value’ => $val,
);
return $form_fields;
}, 10, 2 );
add_filter( ‘attachment_fields_to_save’, function( $post, $attachment ) {
$id = (int) $post[‘ID’];
if ( empty( $_POST[‘mymedia_nonce’] ) || ! wp_verify_nonce( $_POST[‘mymedia_nonce’], ‘mymedia_save_field_’ . $id ) ) {
return $post;
}
if ( current_user_can( ‘edit_post’, $id ) && isset( $attachment[‘mymedia_photographer’] ) ) {
update_post_meta( $id, ‘_mymedia_photographer’, sanitize_text_field( $attachment[‘mymedia_photographer’] ) );
}
return $post;
}, 10, 2 );
add_action( ‘init’, function() {
register_meta( ‘post’, ‘_mymedia_photographer’, array(
‘object_subtype’ => ‘attachment’,
‘show_in_rest’ => array(
‘schema’ => array( ‘type’ => ‘string’, ‘description’ => ‘Photographer credit’ ),
),
‘single’ => true,
‘type’ => ‘string’,
‘auth_callback’ => function() { return current_user_can( ‘upload_files’ ); },
) );
} );
Activate it, and you’re done.
Adding custom media fields makes your content workflow better: credits are automatic, licensing is easy to check, and your site is more consistent. Start with one or two fields, connect them to your templates, and then add more. For performance and version control, use pure PHP. For quick UI, use ACF. For classification, use taxonomies. If you’re making modern front ends, don’t forget the basics: nonce checks, sanitization, capability verification, and REST exposing.
I can customize the code to fit your specific field list (such Photographer, License URL, Usage Notes, Shoot Date, and Editorial Only checkbox) or package it as a plugin that is ready to install with a settings page and exportable JSON.
Elevate your WordPress hosting with 30-day money-back guarantee, free migration, and 24/7 support.
Sign Up TodayMay 15, 2026
Maria
May 8, 2026
Nitish
Before You Go… Get 1 Month FREE on Rocon Hosting!
Experience lightning-fast speeds
No downtime or hidden fees
Dedicated 24/7 expert support
Our team will contact you soon.
Leave a Reply