diff --git src/wp-admin/css/forms.css src/wp-admin/css/forms.css
index f0b1b372ea..8f124d4c60 100644
--- src/wp-admin/css/forms.css
+++ src/wp-admin/css/forms.css
@@ -932,6 +932,11 @@ table.form-table td .updated p {
 	vertical-align: middle;
 }
 
+.form-table.permalink-structure .available-structure-tags li {
+	float: left;
+	margin-right: 5px;
+}
+
 /*------------------------------------------------------------------------------
   21.0 - Network Admin
 ------------------------------------------------------------------------------*/
diff --git src/wp-admin/js/common.js src/wp-admin/js/common.js
index 010a613f11..61a830d96c 100644
--- src/wp-admin/js/common.js
+++ src/wp-admin/js/common.js
@@ -175,6 +175,101 @@ $('.contextual-help-tabs').delegate('a', 'click', function(e) {
 	panel.addClass('active').show();
 });
 
+/**
+ * Update custom permalink structure via buttons.
+ */
+
+var permalinkStructureFocused = false,
+    $permalinkStructure       = $( '#permalink_structure' ),
+    $availableStructureTags   = $( '.form-table.permalink-structure .available-structure-tags button' );
+
+// Check if the permalink structure input field has had focus at least once.
+$permalinkStructure.on( 'focus', function( event ) {
+	permalinkStructureFocused = true;
+	$( this ).off( event );
+} );
+
+/**
+ * Enables or disables a structure tag button depending on its usage.
+ *
+ * If the structure is already used in the custom permalink structure,
+ * it will be disabled.
+ *
+ * @param {object} button Button jQuery object.
+ */
+function changeStructureTagButtonState( button ) {
+	if ( -1 !== $permalinkStructure.val().indexOf( button.text().trim() ) ) {
+		button.attr( 'data-label', button.attr( 'aria-label' ) );
+		button.attr( 'aria-label', button.attr( 'data-used' ) );
+		button.attr( 'aria-pressed', true );
+		button.addClass( 'active' );
+	} else if ( button.attr( 'data-label' ) ) {
+		button.attr( 'aria-label', button.attr( 'data-label' ) );
+		button.attr( 'aria-pressed', false );
+		button.removeClass( 'active' );
+	}
+};
+
+// Check initial button state.
+$availableStructureTags.each( function() {
+	changeStructureTagButtonState( $( this ) );
+} );
+
+// Observe permalink structure field and disable buttons of tags that are already present.
+$permalinkStructure.on( 'change', function() {
+	$availableStructureTags.each( function() {
+		changeStructureTagButtonState( $( this ) );
+	} );
+} );
+
+$availableStructureTags.on( 'click', function() {
+	var permalinkStructureValue = $permalinkStructure.val(),
+	    selectionStart          = $permalinkStructure[ 0 ].selectionStart,
+	    selectionEnd            = $permalinkStructure[ 0 ].selectionEnd,
+	    textToAppend            = $( this ).text().trim(),
+	    textToAnnounce          = $( this ).attr( 'data-added' );
+
+	// Remove structure tag if already part of the structure.
+	if ( -1 !== permalinkStructureValue.indexOf( textToAppend ) ) {
+		permalinkStructureValue = permalinkStructureValue.replace( textToAppend + '/', '' );
+
+		$permalinkStructure.val( '/' === permalinkStructureValue ? '' : permalinkStructureValue );
+
+		// Announce change to screen readers.
+		$( '#custom_selection_updated' ).text( textToAnnounce );
+
+		// Disable button.
+		changeStructureTagButtonState( $( this ) );
+
+		return;
+	}
+
+	// Input field never had focus, move selection to end of input.
+	if ( ! permalinkStructureFocused && 0 === selectionStart && 0 === selectionEnd ) {
+		selectionStart = selectionEnd = permalinkStructureValue.length;
+	}
+
+	$( '#custom_selection' ).prop( 'checked', true );
+
+	// Prepend and append slashes if necessary.
+	if ( '/' !== permalinkStructureValue.substr( 0, selectionStart ).substr( -1 ) ) {
+		textToAppend = '/' + textToAppend;
+	}
+
+	if ( '/' !== permalinkStructureValue.substr( selectionEnd, 1 ) ) {
+		textToAppend = textToAppend + '/';
+	}
+
+	// Insert structure tag at the specified position.
+	$permalinkStructure.val( permalinkStructureValue.substr( 0, selectionStart ) + textToAppend + permalinkStructureValue.substr( selectionEnd ) );
+
+	// Announce change to screen readers.
+	$( '#custom_selection_updated' ).text( textToAnnounce );
+
+	// Disable button.
+	changeStructureTagButtonState( $( this ) );
+} );
+
 $document.ready( function() {
 	var checks, first, last, checked, sliced, mobileEvent, transitionTimeout, focusedRowActions,
 		lastClicked = false,
diff --git src/wp-admin/options-permalink.php src/wp-admin/options-permalink.php
index 95c1a3e0ba..c7193f96a6 100644
--- src/wp-admin/options-permalink.php
+++ src/wp-admin/options-permalink.php
@@ -206,6 +206,69 @@ $structures = array(
 		<td>
 			<code><?php echo get_option('home') . $blog_prefix; ?></code>
 			<input name="permalink_structure" id="permalink_structure" type="text" value="<?php echo esc_attr($permalink_structure); ?>" class="regular-text code" />
+			<div class="available-structure-tags hide-if-no-js">
+				<div id="custom_selection_updated" aria-live="assertive" class="screen-reader-text"></div>
+				<?php
+				$available_tags = array(
+					/* translators: %s: permalink structure tag */
+					'year'     => __( '%s (The year of the post, four digits, for example 2004.)' ),
+					/* translators: %s: permalink structure tag */
+					'monthnum' => __( '%s (Month of the year, for example 05.)' ),
+					/* translators: %s: permalink structure tag */
+					'day'      => __( '%s (Day of the month, for example 28.)' ),
+					/* translators: %s: permalink structure tag */
+					'hour'     => __( '%s (Hour of the day, for example 15.)' ),
+					/* translators: %s: permalink structure tag */
+					'minute'   => __( '%s (Minute of the hour, for example 43.)' ),
+					/* translators: %s: permalink structure tag */
+					'second'   => __( '%s (Second of the minute, for example 33.)' ),
+					/* translators: %s: permalink structure tag */
+					'post_id'  => __( '%s (The unique ID of the post, for example 423.)' ),
+					/* translators: %s: permalink structure tag */
+					'postname' => __( '%s (The sanitized post title (slug).)' ),
+					/* translators: %s: permalink structure tag */
+					'category' => __( '%s (Category slug. Nested sub-categories appear as nested directories in the URI.)' ),
+					/* translators: %s: permalink structure tag */
+					'author'   => __( '%s (A sanitized version of the author name.)' ),
+				);
+
+				/**
+				 * Filters the list of available permalink structure tags on the Permalinks settings page.
+				 *
+				 * @since 4.8.0
+				 *
+				 * @param array $available_tags A key => value pair of available permalink structure tags.
+				 */
+				$available_tags = apply_filters( 'available_permalink_structure_tags', $available_tags );
+
+				/* translators: %s: permalink structure tag */
+				$structure_tag_added = __( '%s added to permalink structure' );
+
+				/* translators: %s: permalink structure tag */
+				$structure_tag_already_used = __( '%s (already used in permalink structure)' );
+
+				if ( ! empty( $available_tags ) ) :
+					?>
+					<p><?php _e( 'Available tags:' ); ?></p>
+					<ul role="list">
+						<?php
+						foreach ( $available_tags as $tag => $explanation ) {
+							?>
+							<li>
+								<button type="button"
+								        class="button button-secondary"
+								        aria-label="<?php echo esc_attr( sprintf( $explanation, $tag ) ); ?>"
+								        data-added="<?php echo esc_attr( sprintf( $structure_tag_added, $tag ) ); ?>"
+								        data-used="<?php echo esc_attr( sprintf( $structure_tag_already_used, $tag ) ); ?>">
+									<?php echo '%' . $tag . '%'; ?>
+								</button>
+							</li>
+							<?php
+						}
+						?>
+					</ul>
+				<?php endif; ?>
+			</div>
 		</td>
 	</tr>
 </table>
