I am saving node as unpublished without adding data in required fields. How can I achieve that in Drupal 8 ? Basically I need something like Save as Draft option for nodes in content types.

  • I guess you would have to do this programmatically. But why do you need to require the fields? Is it an option to make them optional fields? Oct 10, 2017 at 9:46
  • You should add a custom validation function for your "Save unpublished" submit action. But the problem is, that the browsers have an icluded required field vaidation. So you should implement a hook_form_alter and set #required=FALSE on a specific condition.
    – sgoelz
    Oct 10, 2017 at 12:41
  • 2
    @Stef, if this is what I think it is (like my need), this is because fields are still, ultimately, required, but you may want to be able to let the user save it and come back later. May 31, 2018 at 16:38
  • 1
    Does this answer your question? How can I save an unpublished node skipping required fields validation?
    – leymannx
    Oct 9, 2020 at 13:07

2 Answers 2


There's the Require on Publish module. You'll have a checkbox for all fields beneath the default "Required" checkbox that says "Require on publish". So you need to have the default "Required" checkbox unchecked and just check "Require on publish". Quite clever actually.

This module allows fields to be required only when the piece of content is being published or is already published. This can be used when you have fields such as tags or SEO information on your piece of content that editors really don't need to fill in until the content is going live.


When managing a field for a content type, click the new 'Required on Publish' checkbox instead of the 'Required field' checkbox.

Require on Publish checkbox screenshot


The save_draft module should make this possible for Drupal 7 (with the patch from issue 1786442 applied). Issue 2220659 describes a Drupal 8 version, but the patch does not include the fix for allowing missing required fields. In Drupal 8, this has become a bit more difficult because of the added Entity validation.

I created following ugly solution for my use case, which eliminates NotNull errors related to required fields and sets the entity as validated if no errors remain. I make use of disable_html5_validation to disable client side validation.

function find_element_by_path($element, $path) {
  if (count($path) === 0) {
    return $element['widget'];
  if (is_numeric($path[0])) {
    return find_element_by_path($element['widget'][array_shift($path)], $path);
  return find_element_by_path($element[array_shift($path)], $path);

function save_draft_validate($form, &$form_state) {
  // Remove all NotNull validation errors related to required fields.
  $errors = $form_state->getErrors();
  foreach ($errors as $key => $error) {
    if ($error->getUntranslatedString() === 'This value should not be null.') {
      $element = find_element_by_path($form, explode('][', $key));
      if (isset($element['#originallyRequired']) && $element['#originallyRequired']) {
      elseif ($element['#theme'] === 'file_widget_multiple') {
        if (isset($element[0]['#originallyRequired']) && $element[0]['#originallyRequired']) {
    elseif ($error->getUntranslatedString() === 'An illegal choice has been detected. Please contact the site administrator.') {
      $element = find_element_by_path($form, explode('][', $key));
      if (isset($element['#originallyRequired']) && $element['#originallyRequired'] && $element['#value'] === '_none') {
  if (count($errors) === 0) {
    $form_state->setTemporaryValue('entity_validated', TRUE);
  } else {
    foreach($errors as $key => $error) {
      $form_state->setErrorByName($key, $error);

function save_draft_unpublish_submit($form, &$form_state) {
  $form_state->setValue('status', ['value' => 0]);

function remove_required(&$element) {
  // Recurse through all children.
  foreach (Element::children($element) as $key) {
    if (isset($element[$key]) && $element[$key]) {
  // Make sure the title field remains required.
  $titleIds = ['edit-title', 'edit-title-0', 'edit-title-0-value'];
  if (!empty($element['#required']) && !in_array($element['#id'], $titleIds)) {
    $element['#originallyRequired'] = $element['#required'];
    $element['#required'] = FALSE;

function MY_MODULE_form_after_build($form, &$form_state) {
  // Check that the form has been submitted.
  if ($form_state->isProcessingInput()) {
    // If the save draft button was pressed.
    if ($form_state->getTriggeringElement()['#id'] === 'edit-draft') {
  return $form;

function add_draft(&$form) {
  $form['actions']['draft'] = [
    '#type' => 'submit',
    '#value' => t('Save draft'),
    '#weight' => '9',
    '#submit' => $form['actions']['submit']['#submit'],
    '#validate' => ['save_draft_validate'],
  array_unshift($form['actions']['draft']['#submit'], 'save_draft_unpublish_submit');
  $form['#after_build'][] = 'MYMODULE_form_after_build';

 * Implements hook_form_FORM_ID_alter().
function MYMODULE_form_node_MYFORM_form_alter(&$form, FormStateInterface $form_state, $form_id) {

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.