I have a controller in my custom module. Inside this controller I need to render a whole bunch of different things (some basic html, multiple forms, etc). My main problem is I can't figure out how to

  1. Properly load a Drupal 8 custom form
  2. Then get that form into an acceptable format to pass to the #markup output of the controller.
  3. Keep in mind this form is mixed in with other html that will be a mish-mash of lots of stuff not shown here (I've trimmed it down to show a basic example)

My controller code looks like this currently:


namespace Drupal\my_module\Controller;
use Drupal\Core\Controller\ControllerBase;

class SingleNodeProcessor extends ControllerBase {

  public function processSingle($nid) {    
    $markup = '<div class="process-heading">' . $this->t('My custom Form Is below but there will be alot more html than just the form. So this is just an example.') . '</div>';

    $form = $this->formBuilder()->getForm('Drupal\my_module\Form\MyFormClassGoesHere', $nid);
    $markup .= $form; //this is a render array, how do I turn it to html so it can pass to #markup??

    return [
      '#type' => 'markup',
      '#markup' => $markup,
  • 1
    You can return a renderable array, which is how any form should be returned (not as raw HTML). Not every return has to be of type markup.
    – Kevin
    Mar 10, 2017 at 14:33
  • Also you will want to leverage the provided $formBuilder that is injected and provided as part of extending ControllerBase. You don't need to call the static container. See: api.drupal.org/api/drupal/…
    – Kevin
    Mar 10, 2017 at 14:36
  • Right now, $form is a renderable array. I'll also be returning a mix of basic strings/html. I need to combine the form output and the strings/html and output them from this controller. I'm not sure I understand what that looks like. Are you saying that my return #type shouldn't be 'markup'? I've never seen an example where it's not 'markup'.
    – Bobby
    Mar 10, 2017 at 14:52
  • I believe strings would need to be Markup elements which are render arrays. Not literal strings.
    – Kevin
    Mar 10, 2017 at 14:53
  • when pass my bare strings into $markup they cause no problems. when I pass the $form (as render_array) into $markup I get errors of: 'Notice: Array to string conversion in....' so it seems strings are fine, render_arrays aren't.
    – Bobby
    Mar 10, 2017 at 14:59

2 Answers 2


I think you want to return a render array, instead of markup. Because the from needs to stay as a render array, your best bet it to bring your content content to the same format instead of the other way. In your example:

public function processSingle($nid) {
  $build = [];
  $build['heading'] = [
    '#type' => 'markup',
    '#markup' => '<div class="process-heading">' . $this->t('My custom Form Is below but there will be alot more html than just the form. So this is just an example.') . '</div>',
  $build['form'] = $this->formBuilder()->getForm('Drupal\my_module\Form\MyFormClassGoesHere', $nid);
  return $build;
  • 1
    This looks promising. But the problem in my case might be that the form is within html. So there might be some markup before it, some markup after it. Along with that (something I didn't mention) is sometimes the form shows, sometimes it doesn't, so it's inclusion is subject to logic as well. Because of all this, I'm thinking I need to do all this in #theme (where I can more easily use complex functions) instead of #markup. Thoughts?
    – Bobby
    Mar 10, 2017 at 16:40
  • @Bobby, Dave's answer is right, you should start from there and then you can use more advance things like nested render elements or custom templates and nest the form somewhere inside of the render structure where you need it. If you want to change the inside of the form, that means the content between the form tags, then do this in the form class.
    – 4uk4
    Mar 10, 2017 at 16:58
  • Just like in my example, you can add additional render array #markup for things before and after the form. You could even set $build['form']['#access'] = FALSE; yourself to hide the form when necessary.
    – Dave Reid
    Mar 11, 2017 at 16:56

The problem is that render arrays of the #markup type run the resulting markup through XSS filtering, thereby filtering out many tags. (see Render API docs). In this case it will filter out the form tags.

The correct way to do this, according to the docs:

If your markup needs any of the tags not in this whitelist, then you can implement a theme hook and/or an asset library. Alternatively, you can use the key #allowed_tags to alter which tags are filtered.

So the solution is to either pass some tags to #allowed_tags in the render array or (the better solution), use #theme in your render array instead of simple #markup.

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.