Custom Form Renderers¶
Overview¶
XOOPS allows customization of form rendering through custom renderers. This enables theme-specific styling, accessibility improvements, and integration with frontend frameworks like Bootstrap or Tailwind CSS.
Default Rendering¶
By default, XOOPS forms use the XoopsFormRenderer class which outputs basic HTML:
// Default rendering
$form = new XoopsThemeForm('My Form', 'myform', 'submit.php');
$form->addElement(new XoopsFormText('Name', 'name', 50, 255));
echo $form->render();
Custom Renderer Architecture¶
classDiagram
class XoopsFormRenderer {
<<interface>>
+renderForm(XoopsForm form)
+renderElement(XoopsFormElement element)
+renderLabel(string caption)
}
XoopsFormRenderer <|-- XoopsFormRendererBootstrap4
XoopsFormRenderer <|-- XoopsFormRendererBootstrap5
XoopsFormRenderer <|-- XoopsFormRendererTailwind
XoopsFormRenderer <|-- CustomFormRenderer Creating a Custom Renderer¶
Basic Renderer Class¶
namespace Xoops\Modules\MyModule\Form;
use XoopsFormRenderer;
use XoopsForm;
use XoopsFormElement;
class BootstrapRenderer extends XoopsFormRenderer
{
public function renderFormStart(XoopsForm $form): string
{
$class = $form->getExtra() ?: 'needs-validation';
return sprintf(
'<form name="%s" id="%s" action="%s" method="%s" class="%s" %s>',
$form->getName(),
$form->getName(),
$form->getAction(),
$form->getMethod(),
$class,
$form->getExtra()
);
}
public function renderFormEnd(): string
{
return '</form>';
}
public function renderElement(XoopsFormElement $element): string
{
$output = '<div class="mb-3">';
// Label
if ($element->getCaption()) {
$output .= sprintf(
'<label for="%s" class="form-label">%s</label>',
$element->getName(),
$element->getCaption()
);
}
// Element with Bootstrap classes
$element->setExtra($element->getExtra() . ' class="form-control"');
$output .= $element->render();
// Description
if ($element->getDescription()) {
$output .= sprintf(
'<div class="form-text">%s</div>',
$element->getDescription()
);
}
$output .= '</div>';
return $output;
}
public function renderButton(XoopsFormElement $button): string
{
$type = $button->getType() === 'submit' ? 'btn-primary' : 'btn-secondary';
return sprintf(
'<button type="%s" name="%s" class="btn %s">%s</button>',
$button->getType(),
$button->getName(),
$type,
$button->getValue()
);
}
}
Registering the Renderer¶
// In your module's xoops_version.php or bootstrap
$GLOBALS['xoopsOption']['form_renderer'] = new BootstrapRenderer();
// Or set it per-form
$form = new XoopsThemeForm('My Form', 'myform', 'submit.php');
$form->setRenderer(new BootstrapRenderer());
Built-in Renderers¶
Bootstrap 4 Renderer¶
Bootstrap 5 Renderer¶
use Xoops\Form\Renderer\Bootstrap5Renderer;
$form->setRenderer(new Bootstrap5Renderer([
'floating_labels' => true,
'validation_style' => 'tooltip'
]));
Rendering Specific Elements¶
Custom Select Renderer¶
public function renderSelect(XoopsFormSelect $select): string
{
$multiple = $select->isMultiple() ? 'multiple' : '';
$size = $select->getSize();
$output = sprintf(
'<select name="%s%s" id="%s" class="form-select" %s size="%d">',
$select->getName(),
$multiple ? '[]' : '',
$select->getName(),
$multiple,
$size
);
foreach ($select->getOptions() as $value => $label) {
$selected = in_array($value, (array)$select->getValue()) ? 'selected' : '';
$output .= sprintf(
'<option value="%s" %s>%s</option>',
htmlspecialchars($value),
$selected,
htmlspecialchars($label)
);
}
$output .= '</select>';
return $output;
}
Custom File Input Renderer¶
public function renderFile(XoopsFormFile $file): string
{
return sprintf(
'<div class="mb-3">
<label for="%s" class="form-label">%s</label>
<input type="file" class="form-control" id="%s" name="%s" %s>
</div>',
$file->getName(),
$file->getCaption(),
$file->getName(),
$file->getName(),
$file->getExtra()
);
}
Theme Integration¶
In Theme Template¶
{* In theme's form.tpl *}
{foreach $form.elements as $element}
<div class="form-group {$element.class}">
{if $element.caption}
<label class="control-label">{$element.caption}</label>
{/if}
{$element.body}
{if $element.description}
<span class="help-block">{$element.description}</span>
{/if}
</div>
{/foreach}
Best Practices¶
- Inherit from base renderer - Extend
XoopsFormRendererfor consistency - Support all element types - Handle text, select, checkbox, radio, etc.
- Accessibility - Include proper labels, ARIA attributes
- Validation styles - Show error states appropriately
- Responsive design - Ensure forms work on mobile