{"id":15021744,"url":"https://github.com/symfony/ux-toggle-password","last_synced_at":"2026-04-02T00:04:13.814Z","repository":{"id":185615907,"uuid":"673207656","full_name":"symfony/ux-toggle-password","owner":"symfony","description":"Toggle visibility of password inputs for Symfony Forms","archived":false,"fork":false,"pushed_at":"2025-09-21T19:05:00.000Z","size":104,"stargazers_count":25,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"2.x","last_synced_at":"2025-09-21T21:09:31.521Z","etag":null,"topics":["javascript","symfony","symfony-ux","ux"],"latest_commit_sha":null,"homepage":"https://symfony.com/bundles/ux-toggle-password","language":"PHP","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/symfony.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"fabpot","tidelift":"packagist/symfony/symfony","custom":"https://symfony.com/sponsor"}},"created_at":"2023-08-01T05:46:59.000Z","updated_at":"2025-09-21T19:05:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"6732aa4c-50bf-4bd2-bb03-e141e9b57759","html_url":"https://github.com/symfony/ux-toggle-password","commit_stats":{"total_commits":20,"total_committers":12,"mean_commits":"1.6666666666666667","dds":0.8,"last_synced_commit":"4d7d9a70bac309c8e388b09528094a59cd1a01ab"},"previous_names":["symfony/ux-toggle-password"],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/symfony/ux-toggle-password","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symfony%2Fux-toggle-password","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symfony%2Fux-toggle-password/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symfony%2Fux-toggle-password/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symfony%2Fux-toggle-password/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/symfony","download_url":"https://codeload.github.com/symfony/ux-toggle-password/tar.gz/refs/heads/2.x","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/symfony%2Fux-toggle-password/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279010014,"owners_count":26084682,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","symfony","symfony-ux","ux"],"created_at":"2024-09-24T19:56:58.834Z","updated_at":"2026-04-02T00:04:13.801Z","avatar_url":"https://github.com/symfony.png","language":"PHP","funding_links":["https://github.com/sponsors/fabpot","https://tidelift.com/funding/github/packagist/symfony/symfony","https://symfony.com/sponsor"],"categories":[],"sub_categories":[],"readme":"# Symfony UX TogglePassword\n\n\u003e [!WARNING]\n\u003e **Deprecated**: This package has been **deprecated** in 2.x and will be removed in the next major version.\n\nTo keep the same functionality in your Symfony application, follow these migration steps:\n\n1. Remove the `symfony/ux-toggle-password` package from your project:\n\n```bash\ncomposer remove symfony/ux-toggle-password\n```\n\n2. Create the following files in your project:\n\n\u003e [!NOTE]\n\u003e These files are provided as a reference.\n\u003e You can customize them to fit your needs, and even simplify the implementation if you don't need all the features.\n\n- `src/Form/Extension/TogglePasswordTypeExtension.php`\n\n```php\n\u003c?php\n\nnamespace App\\Form\\Extension;\n\nuse Symfony\\Component\\Form\\AbstractTypeExtension;\nuse Symfony\\Component\\Form\\Extension\\Core\\Type\\PasswordType;\nuse Symfony\\Component\\Form\\FormInterface;\nuse Symfony\\Component\\Form\\FormView;\nuse Symfony\\Component\\OptionsResolver\\Options;\nuse Symfony\\Component\\OptionsResolver\\OptionsResolver;\nuse Symfony\\Component\\Translation\\TranslatableMessage;\nuse Symfony\\Contracts\\Translation\\TranslatorInterface;\n\nfinal class TogglePasswordTypeExtension extends AbstractTypeExtension\n{\n    public function __construct(private readonly ?TranslatorInterface $translator)\n    {\n    }\n\n    public static function getExtendedTypes(): iterable\n    {\n        return [PasswordType::class];\n    }\n\n    public function configureOptions(OptionsResolver $resolver): void\n    {\n        $resolver-\u003esetDefaults([\n            'toggle' =\u003e false,\n            'hidden_label' =\u003e 'Hide',\n            'visible_label' =\u003e 'Show',\n            'hidden_icon' =\u003e 'Default',\n            'visible_icon' =\u003e 'Default',\n            'button_classes' =\u003e ['toggle-password-button'],\n            'toggle_container_classes' =\u003e ['toggle-password-container'],\n            'toggle_translation_domain' =\u003e null,\n            'use_toggle_form_theme' =\u003e true,\n        ]);\n\n        $resolver-\u003esetNormalizer(\n            'toggle_translation_domain',\n            static fn (Options $options, $labelTranslationDomain) =\u003e $labelTranslationDomain ?? $options['translation_domain'],\n        );\n\n        $resolver-\u003esetAllowedTypes('toggle', ['bool']);\n        $resolver-\u003esetAllowedTypes('hidden_label', ['string', TranslatableMessage::class, 'null']);\n        $resolver-\u003esetAllowedTypes('visible_label', ['string', TranslatableMessage::class, 'null']);\n        $resolver-\u003esetAllowedTypes('hidden_icon', ['string', 'null']);\n        $resolver-\u003esetAllowedTypes('visible_icon', ['string', 'null']);\n        $resolver-\u003esetAllowedTypes('button_classes', ['string[]']);\n        $resolver-\u003esetAllowedTypes('toggle_container_classes', ['string[]']);\n        $resolver-\u003esetAllowedTypes('toggle_translation_domain', ['string', 'bool', 'null']);\n        $resolver-\u003esetAllowedTypes('use_toggle_form_theme', ['bool']);\n    }\n\n    public function buildView(FormView $view, FormInterface $form, array $options): void\n    {\n        $view-\u003evars['toggle'] = $options['toggle'];\n\n        if (!$options['toggle']) {\n            return;\n        }\n\n        if ($options['use_toggle_form_theme']) {\n            array_splice($view-\u003evars['block_prefixes'], -1, 0, 'toggle_password');\n        }\n\n        $controllerName = 'toggle-password';\n        $view-\u003evars['attr']['data-controller'] = trim(\\sprintf('%s %s', $view-\u003evars['attr']['data-controller'] ?? '', $controllerName));\n\n        if (false !== $options['toggle_translation_domain']) {\n            $controllerValues['hidden-label'] = $this-\u003etranslateLabel($options['hidden_label'], $options['toggle_translation_domain']);\n            $controllerValues['visible-label'] = $this-\u003etranslateLabel($options['visible_label'], $options['toggle_translation_domain']);\n        } else {\n            $controllerValues['hidden-label'] = $options['hidden_label'];\n            $controllerValues['visible-label'] = $options['visible_label'];\n        }\n\n        $controllerValues['hidden-icon'] = $options['hidden_icon'];\n        $controllerValues['visible-icon'] = $options['visible_icon'];\n        $controllerValues['button-classes'] = json_encode($options['button_classes'], \\JSON_THROW_ON_ERROR);\n\n        foreach ($controllerValues as $name =\u003e $value) {\n            $view-\u003evars['attr'][\\sprintf('data-%s-%s-value', $controllerName, $name)] = $value;\n        }\n\n        $view-\u003evars['toggle_container_classes'] = $options['toggle_container_classes'];\n    }\n\n    private function translateLabel(string|TranslatableMessage|null $label, ?string $translationDomain): ?string\n    {\n        if (null === $this-\u003etranslator || null === $label) {\n            return $label;\n        }\n\n        if ($label instanceof TranslatableMessage) {\n            return $label-\u003etrans($this-\u003etranslator);\n        }\n\n        return $this-\u003etranslator-\u003etrans($label, domain: $translationDomain);\n    }\n}\n```\n\n- `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.\n\n```twig\n{%- block toggle_password_widget -%}\n    \u003cdiv class=\"{{ toggle_container_classes|join(' ') }}\"\u003e{{ block('password_widget') }}\u003c/div\u003e\n{%- endblock toggle_password_widget -%}\n```\n\n- `assets/controllers/toggle_password_controller.js`\n\n```javascript\nimport { Controller } from '@hotwired/stimulus';\nimport '../styles/toggle_password.css';\n\nexport default class extends Controller {\n    static values = {\n        visibleLabel: { type: String, default: 'Show' },\n        visibleIcon: { type: String, default: 'Default' },\n        hiddenLabel: { type: String, default: 'Hide' },\n        hiddenIcon: { type: String, default: 'Default' },\n        buttonClasses: Array,\n    };\n\n    isDisplayed = false;\n    visibleIcon = `\u003csvg xmlns=\"http://www.w3.org/2000/svg\" class=\"toggle-password-icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"\u003e\n\u003cpath d=\"M10 12a2 2 0 100-4 2 2 0 000 4z\" /\u003e\n\u003cpath fill-rule=\"evenodd\" d=\"M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z\" clip-rule=\"evenodd\" /\u003e\n\u003c/svg\u003e`;\n    hiddenIcon = `\u003csvg xmlns=\"http://www.w3.org/2000/svg\" class=\"toggle-password-icon\" viewBox=\"0 0 20 20\" fill=\"currentColor\"\u003e\n\u003cpath fill-rule=\"evenodd\" d=\"M3.707 2.293a1 1 0 00-1.414 1.414l14 14a1 1 0 001.414-1.414l-1.473-1.473A10.014 10.014 0 0019.542 10C18.268 5.943 14.478 3 10 3a9.958 9.958 0 00-4.512 1.074l-1.78-1.781zm4.261 4.26l1.514 1.515a2.003 2.003 0 012.45 2.45l1.514 1.514a4 4 0 00-5.478-5.478z\" clip-rule=\"evenodd\" /\u003e\n\u003cpath d=\"M12.454 16.697L9.75 13.992a4 4 0 01-3.742-3.741L2.335 6.578A9.98 9.98 0 00.458 10c1.274 4.057 5.065 7 9.542 7 .847 0 1.669-.105 2.454-.303z\" /\u003e\n\u003c/svg\u003e`;\n\n    connect() {\n        if (this.visibleIconValue !== 'Default') {\n            this.visibleIcon = this.visibleIconValue;\n        }\n\n        if (this.hiddenIconValue !== 'Default') {\n            this.hiddenIcon = this.hiddenIconValue;\n        }\n\n        const button = this.createButton();\n\n        this.element.insertAdjacentElement('afterend', button);\n        this.dispatchEvent('connect', { element: this.element, button });\n    }\n\n    /**\n     * @returns {HTMLButtonElement}\n     */\n    createButton() {\n        const button = document.createElement('button');\n        button.type = 'button';\n        button.classList.add(...this.buttonClassesValue);\n        button.setAttribute('tabindex', '-1');\n        button.addEventListener('click', this.toggle.bind(this));\n        button.innerHTML = `${this.visibleIcon} ${this.visibleLabelValue}`;\n        return button;\n    }\n\n    /**\n     * Toggle input type between \"text\" or \"password\" and update label accordingly\n     */\n    toggle(event) {\n        this.isDisplayed = !this.isDisplayed;\n        const toggleButtonElement = event.currentTarget;\n        toggleButtonElement.innerHTML = this.isDisplayed\n            ? `${this.hiddenIcon} ${this.hiddenLabelValue}`\n            : `${this.visibleIcon} ${this.visibleLabelValue}`;\n        this.element.setAttribute('type', this.isDisplayed ? 'text' : 'password');\n        this.dispatchEvent(this.isDisplayed ? 'show' : 'hide', { element: this.element, button: toggleButtonElement });\n    }\n\n    dispatchEvent(name, payload) {\n        this.dispatch(name, { detail: payload, prefix: 'toggle-password' });\n    }\n}\n```\n\n- `assets/styles/toggle_password.css`\n\n```css\n.toggle-password-container {\n    position: relative;\n}\n.toggle-password-icon {\n    height: 1rem;\n    width: 1rem;\n}\n.toggle-password-button {\n    align-items: center;\n    background-color: transparent;\n    border: none;\n    column-gap: 0.25rem;\n    display: flex;\n    flex-direction: row;\n    font-size: 0.875rem;\n    justify-items: center;\n    height: 1rem;\n    line-height: 1.25rem;\n    position: absolute;\n    right: 0.5rem;\n    top: -1.25rem;\n}\n```\n\nYou're done!\n\n---\n\nSymfony UX TogglePassword is a Symfony bundle providing visibility toggle for password inputs\nin Symfony Forms. It is part of [the Symfony UX initiative](https://ux.symfony.com/).\n\nIt allows visitors to switch the type of password field to text and vice versa.\n\n**This repository is a READ-ONLY sub-tree split**. See\nhttps://github.com/symfony/ux to create issues or submit pull requests.\n\n## Sponsor\n\nThe Symfony UX packages are [backed][1] by [Mercure.rocks][2].\n\nCreate real-time experiences in minutes! Mercure.rocks provides a realtime API service\nthat is tightly integrated with Symfony: create UIs that update in live with UX Turbo,\nsend notifications with the Notifier component, expose async APIs with API Platform and\ncreate low level stuffs with the Mercure component. We maintain and scale the complex\ninfrastructure for you!\n\nHelp Symfony by [sponsoring][3] its development!\n\n## Resources\n\n- [Documentation](https://symfony.com/bundles/ux-toggle-password/current/index.html)\n- [Report issues](https://github.com/symfony/ux/issues) and\n  [send Pull Requests](https://github.com/symfony/ux/pulls)\n  in the [main Symfony UX repository](https://github.com/symfony/ux)\n\n[1]: https://symfony.com/backers\n[2]: https://mercure.rocks\n[3]: https://symfony.com/sponsor\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsymfony%2Fux-toggle-password","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsymfony%2Fux-toggle-password","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsymfony%2Fux-toggle-password/lists"}