https://github.com/symfony/ux-toggle-password
Toggle visibility of password inputs for Symfony Forms
https://github.com/symfony/ux-toggle-password
javascript symfony symfony-ux ux
Last synced: 5 months ago
JSON representation
Toggle visibility of password inputs for Symfony Forms
- Host: GitHub
- URL: https://github.com/symfony/ux-toggle-password
- Owner: symfony
- License: mit
- Created: 2023-08-01T05:46:59.000Z (over 2 years ago)
- Default Branch: 2.x
- Last Pushed: 2025-09-21T19:05:00.000Z (6 months ago)
- Last Synced: 2025-09-21T21:09:31.521Z (6 months ago)
- Topics: javascript, symfony, symfony-ux, ux
- Language: PHP
- Homepage: https://symfony.com/bundles/ux-toggle-password
- Size: 102 KB
- Stars: 25
- Watchers: 4
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Symfony UX TogglePassword
> [!WARNING]
> **Deprecated**: This package has been **deprecated** in 2.x and will be removed in the next major version.
To keep the same functionality in your Symfony application, follow these migration steps:
1. Remove the `symfony/ux-toggle-password` package from your project:
```bash
composer remove symfony/ux-toggle-password
```
2. Create the following files in your project:
> [!NOTE]
> These files are provided as a reference.
> You can customize them to fit your needs, and even simplify the implementation if you don't need all the features.
- `src/Form/Extension/TogglePasswordTypeExtension.php`
```php
setDefaults([
'toggle' => false,
'hidden_label' => 'Hide',
'visible_label' => 'Show',
'hidden_icon' => 'Default',
'visible_icon' => 'Default',
'button_classes' => ['toggle-password-button'],
'toggle_container_classes' => ['toggle-password-container'],
'toggle_translation_domain' => null,
'use_toggle_form_theme' => true,
]);
$resolver->setNormalizer(
'toggle_translation_domain',
static fn (Options $options, $labelTranslationDomain) => $labelTranslationDomain ?? $options['translation_domain'],
);
$resolver->setAllowedTypes('toggle', ['bool']);
$resolver->setAllowedTypes('hidden_label', ['string', TranslatableMessage::class, 'null']);
$resolver->setAllowedTypes('visible_label', ['string', TranslatableMessage::class, 'null']);
$resolver->setAllowedTypes('hidden_icon', ['string', 'null']);
$resolver->setAllowedTypes('visible_icon', ['string', 'null']);
$resolver->setAllowedTypes('button_classes', ['string[]']);
$resolver->setAllowedTypes('toggle_container_classes', ['string[]']);
$resolver->setAllowedTypes('toggle_translation_domain', ['string', 'bool', 'null']);
$resolver->setAllowedTypes('use_toggle_form_theme', ['bool']);
}
public function buildView(FormView $view, FormInterface $form, array $options): void
{
$view->vars['toggle'] = $options['toggle'];
if (!$options['toggle']) {
return;
}
if ($options['use_toggle_form_theme']) {
array_splice($view->vars['block_prefixes'], -1, 0, 'toggle_password');
}
$controllerName = 'toggle-password';
$view->vars['attr']['data-controller'] = trim(\sprintf('%s %s', $view->vars['attr']['data-controller'] ?? '', $controllerName));
if (false !== $options['toggle_translation_domain']) {
$controllerValues['hidden-label'] = $this->translateLabel($options['hidden_label'], $options['toggle_translation_domain']);
$controllerValues['visible-label'] = $this->translateLabel($options['visible_label'], $options['toggle_translation_domain']);
} else {
$controllerValues['hidden-label'] = $options['hidden_label'];
$controllerValues['visible-label'] = $options['visible_label'];
}
$controllerValues['hidden-icon'] = $options['hidden_icon'];
$controllerValues['visible-icon'] = $options['visible_icon'];
$controllerValues['button-classes'] = json_encode($options['button_classes'], \JSON_THROW_ON_ERROR);
foreach ($controllerValues as $name => $value) {
$view->vars['attr'][\sprintf('data-%s-%s-value', $controllerName, $name)] = $value;
}
$view->vars['toggle_container_classes'] = $options['toggle_container_classes'];
}
private function translateLabel(string|TranslatableMessage|null $label, ?string $translationDomain): ?string
{
if (null === $this->translator || null === $label) {
return $label;
}
if ($label instanceof TranslatableMessage) {
return $label->trans($this->translator);
}
return $this->translator->trans($label, domain: $translationDomain);
}
}
```
- `template/form_theme.html.twig`, and follow the [How to work with form themes](https://symfony.com/doc/current/form/form_themes.html) documentation to register it.
```twig
{%- block toggle_password_widget -%}
{{ block('password_widget') }}
{%- endblock toggle_password_widget -%}
```
- `assets/controllers/toggle_password_controller.js`
```javascript
import { Controller } from '@hotwired/stimulus';
import '../styles/toggle_password.css';
export default class extends Controller {
static values = {
visibleLabel: { type: String, default: 'Show' },
visibleIcon: { type: String, default: 'Default' },
hiddenLabel: { type: String, default: 'Hide' },
hiddenIcon: { type: String, default: 'Default' },
buttonClasses: Array,
};
isDisplayed = false;
visibleIcon = `
`;
hiddenIcon = `
`;
connect() {
if (this.visibleIconValue !== 'Default') {
this.visibleIcon = this.visibleIconValue;
}
if (this.hiddenIconValue !== 'Default') {
this.hiddenIcon = this.hiddenIconValue;
}
const button = this.createButton();
this.element.insertAdjacentElement('afterend', button);
this.dispatchEvent('connect', { element: this.element, button });
}
/**
* @returns {HTMLButtonElement}
*/
createButton() {
const button = document.createElement('button');
button.type = 'button';
button.classList.add(...this.buttonClassesValue);
button.setAttribute('tabindex', '-1');
button.addEventListener('click', this.toggle.bind(this));
button.innerHTML = `${this.visibleIcon} ${this.visibleLabelValue}`;
return button;
}
/**
* Toggle input type between "text" or "password" and update label accordingly
*/
toggle(event) {
this.isDisplayed = !this.isDisplayed;
const toggleButtonElement = event.currentTarget;
toggleButtonElement.innerHTML = this.isDisplayed
? `${this.hiddenIcon} ${this.hiddenLabelValue}`
: `${this.visibleIcon} ${this.visibleLabelValue}`;
this.element.setAttribute('type', this.isDisplayed ? 'text' : 'password');
this.dispatchEvent(this.isDisplayed ? 'show' : 'hide', { element: this.element, button: toggleButtonElement });
}
dispatchEvent(name, payload) {
this.dispatch(name, { detail: payload, prefix: 'toggle-password' });
}
}
```
- `assets/styles/toggle_password.css`
```css
.toggle-password-container {
position: relative;
}
.toggle-password-icon {
height: 1rem;
width: 1rem;
}
.toggle-password-button {
align-items: center;
background-color: transparent;
border: none;
column-gap: 0.25rem;
display: flex;
flex-direction: row;
font-size: 0.875rem;
justify-items: center;
height: 1rem;
line-height: 1.25rem;
position: absolute;
right: 0.5rem;
top: -1.25rem;
}
```
You're done!
---
Symfony UX TogglePassword is a Symfony bundle providing visibility toggle for password inputs
in Symfony Forms. It is part of [the Symfony UX initiative](https://ux.symfony.com/).
It allows visitors to switch the type of password field to text and vice versa.
**This repository is a READ-ONLY sub-tree split**. See
https://github.com/symfony/ux to create issues or submit pull requests.
## Sponsor
The Symfony UX packages are [backed][1] by [Mercure.rocks][2].
Create real-time experiences in minutes! Mercure.rocks provides a realtime API service
that is tightly integrated with Symfony: create UIs that update in live with UX Turbo,
send notifications with the Notifier component, expose async APIs with API Platform and
create low level stuffs with the Mercure component. We maintain and scale the complex
infrastructure for you!
Help Symfony by [sponsoring][3] its development!
## Resources
- [Documentation](https://symfony.com/bundles/ux-toggle-password/current/index.html)
- [Report issues](https://github.com/symfony/ux/issues) and
[send Pull Requests](https://github.com/symfony/ux/pulls)
in the [main Symfony UX repository](https://github.com/symfony/ux)
[1]: https://symfony.com/backers
[2]: https://mercure.rocks
[3]: https://symfony.com/sponsor