CINXE.COM

Sign-in form best practices  |  Articles  |  web.dev

<!doctype html> <html lang="en" dir="ltr"> <head> <meta name="google-signin-client-id" content="157101835696-ooapojlodmuabs2do2vuhhnf90bccmoi.apps.googleusercontent.com"> <meta name="google-signin-scope" content="profile email https://www.googleapis.com/auth/developerprofiles https://www.googleapis.com/auth/developerprofiles.award"> <meta property="og:site_name" content="web.dev"> <meta property="og:type" content="website"><meta name="theme-color" content="#3740ff"><meta charset="utf-8"> <meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="manifest" href="/_pwa/web/manifest.json" crossorigin="use-credentials"> <link rel="preconnect" href="//www.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.gstatic.com" crossorigin> <link rel="preconnect" href="//fonts.googleapis.com" crossorigin> <link rel="preconnect" href="//apis.google.com" crossorigin> <link rel="preconnect" href="//www.google-analytics.com" crossorigin><link rel="stylesheet" href="//fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap"> <link rel="stylesheet" href="//fonts.googleapis.com/css2?family=Material+Icons&family=Material+Symbols+Outlined&display=block"><link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/css/app.css"> <link rel="stylesheet" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/css/dark-theme.css" disabled> <link rel="shortcut icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/favicon.png"> <link rel="apple-touch-icon" href="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/touchicon-180.png"><link rel="canonical" href="https://web.dev/articles/sign-in-form-best-practices"><link rel="search" type="application/opensearchdescription+xml" title="web.dev" href="https://web.dev/s/opensearch.xml"> <link rel="alternate" hreflang="en" href="https://web.dev/articles/sign-in-form-best-practices" /><link rel="alternate" hreflang="x-default" href="https://web.dev/articles/sign-in-form-best-practices" /><link rel="alternate" hreflang="ar" href="https://web.dev/articles/sign-in-form-best-practices?hl=ar" /><link rel="alternate" hreflang="bn" href="https://web.dev/articles/sign-in-form-best-practices?hl=bn" /><link rel="alternate" hreflang="zh-Hans" href="https://web.dev/articles/sign-in-form-best-practices?hl=zh-cn" /><link rel="alternate" hreflang="zh-Hant" href="https://web.dev/articles/sign-in-form-best-practices?hl=zh-tw" /><link rel="alternate" hreflang="fa" href="https://web.dev/articles/sign-in-form-best-practices?hl=fa" /><link rel="alternate" hreflang="fr" href="https://web.dev/articles/sign-in-form-best-practices?hl=fr" /><link rel="alternate" hreflang="de" href="https://web.dev/articles/sign-in-form-best-practices?hl=de" /><link rel="alternate" hreflang="he" href="https://web.dev/articles/sign-in-form-best-practices?hl=he" /><link rel="alternate" hreflang="hi" href="https://web.dev/articles/sign-in-form-best-practices?hl=hi" /><link rel="alternate" hreflang="id" href="https://web.dev/articles/sign-in-form-best-practices?hl=id" /><link rel="alternate" hreflang="it" href="https://web.dev/articles/sign-in-form-best-practices?hl=it" /><link rel="alternate" hreflang="ja" href="https://web.dev/articles/sign-in-form-best-practices?hl=ja" /><link rel="alternate" hreflang="ko" href="https://web.dev/articles/sign-in-form-best-practices?hl=ko" /><link rel="alternate" hreflang="pl" href="https://web.dev/articles/sign-in-form-best-practices?hl=pl" /><link rel="alternate" hreflang="pt-BR" href="https://web.dev/articles/sign-in-form-best-practices?hl=pt-br" /><link rel="alternate" hreflang="ru" href="https://web.dev/articles/sign-in-form-best-practices?hl=ru" /><link rel="alternate" hreflang="es" href="https://web.dev/articles/sign-in-form-best-practices?hl=es" /><link rel="alternate" hreflang="es-419" href="https://web.dev/articles/sign-in-form-best-practices?hl=es-419" /><link rel="alternate" hreflang="th" href="https://web.dev/articles/sign-in-form-best-practices?hl=th" /><link rel="alternate" hreflang="tr" href="https://web.dev/articles/sign-in-form-best-practices?hl=tr" /><link rel="alternate" hreflang="vi" href="https://web.dev/articles/sign-in-form-best-practices?hl=vi" /><link rel="alternate" hreflang="en-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices" /><link rel="alternate" hreflang="x-default" href="https://web.developers.google.cn/articles/sign-in-form-best-practices" /><link rel="alternate" hreflang="ar-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=ar" /><link rel="alternate" hreflang="bn-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=bn" /><link rel="alternate" hreflang="zh-Hans-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=zh-cn" /><link rel="alternate" hreflang="zh-Hant-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=zh-tw" /><link rel="alternate" hreflang="fa-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=fa" /><link rel="alternate" hreflang="fr-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=fr" /><link rel="alternate" hreflang="de-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=de" /><link rel="alternate" hreflang="he-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=he" /><link rel="alternate" hreflang="hi-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=hi" /><link rel="alternate" hreflang="id-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=id" /><link rel="alternate" hreflang="it-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=it" /><link rel="alternate" hreflang="ja-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=ja" /><link rel="alternate" hreflang="ko-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=ko" /><link rel="alternate" hreflang="pl-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=pl" /><link rel="alternate" hreflang="pt-BR-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=pt-br" /><link rel="alternate" hreflang="ru-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=ru" /><link rel="alternate" hreflang="es-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=es" /><link rel="alternate" hreflang="es-419-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=es-419" /><link rel="alternate" hreflang="th-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=th" /><link rel="alternate" hreflang="tr-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=tr" /><link rel="alternate" hreflang="vi-cn" href="https://web.developers.google.cn/articles/sign-in-form-best-practices?hl=vi" /><title>Sign-in form best practices &nbsp;|&nbsp; Articles &nbsp;|&nbsp; web.dev</title> <meta property="og:title" content="Sign-in form best practices &nbsp;|&nbsp; Articles &nbsp;|&nbsp; web.dev"><meta name="description" content="Use cross-platform browser features to build sign-in forms that are secure, accessible and easy to use."> <meta property="og:description" content="Use cross-platform browser features to build sign-in forms that are secure, accessible and easy to use."><meta property="og:url" content="https://web.dev/articles/sign-in-form-best-practices"><meta property="og:locale" content="en"><script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "dateModified": "2020-06-29", "headline": "Sign-in form best practices" } </script><script type="application/ld+json"> { "@context": "https://schema.org", "@type": "BreadcrumbList", "itemListElement": [{ "@type": "ListItem", "position": 1, "name": "Articles", "item": "https://web.dev/articles" },{ "@type": "ListItem", "position": 2, "name": "Sign-in form best practices", "item": "https://web.dev/articles/sign-in-form-best-practices" }] } </script> <link rel="stylesheet" href="/extras.css"></head> <body class="" template="page" theme="web-theme" type="article" appearance layout="docs" display-toc pending> <devsite-progress type="indeterminate" id="app-progress"></devsite-progress> <section class="devsite-wrapper"> <devsite-cookie-notification-bar></devsite-cookie-notification-bar><devsite-header role="banner"> <div class="devsite-header--inner nocontent"> <div class="devsite-top-logo-row-wrapper-wrapper"> <div class="devsite-top-logo-row-wrapper"> <div class="devsite-top-logo-row"> <button type="button" id="devsite-hamburger-menu" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Navigation menu button" visually-hidden aria-label="Open menu"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="webDev" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <source srcset="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup-dark-theme.svg" media="(prefers-color-scheme: dark)" class="devsite-dark-theme" alt="web.dev"> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup.svg" class="devsite-site-logo" alt="web.dev"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> <div class="devsite-top-logo-row-middle"> <div class="devsite-header-upper-tabs"> <devsite-tabs class="upper-tabs"> <nav class="devsite-tabs-wrapper" aria-label="Upper tabs"> <tab > <a href="https://web.dev/about" track-metadata-eventdetail="https://web.dev/about" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - about" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: About" track-name="about" > About </a> </tab> <tab > <a href="https://web.dev/html" track-metadata-eventdetail="https://web.dev/html" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - html" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: HTML" track-name="html" > HTML </a> </tab> <tab > <a href="https://web.dev/css" track-metadata-eventdetail="https://web.dev/css" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - css" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: CSS" track-name="css" > CSS </a> </tab> <tab > <a href="https://web.dev/javascript" track-metadata-eventdetail="https://web.dev/javascript" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - javascript" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: JavaScript" track-name="javascript" > JavaScript </a> </tab> <tab > <a href="https://web.dev/blog" track-metadata-eventdetail="https://web.dev/blog" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - blog" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Blog" track-name="blog" > Blog </a> </tab> <tab > <a href="https://web.dev/learn" track-metadata-eventdetail="https://web.dev/learn" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - learn" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Learn" track-name="learn" > Learn </a> </tab> <tab class="devsite-active"> <a href="https://web.dev/explore" track-metadata-eventdetail="https://web.dev/explore" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - explore" track-metadata-module="primary nav" aria-label="Explore, selected" data-category="Site-Wide Custom Events" data-label="Tab: Explore" track-name="explore" > Explore </a> </tab> <tab > <a href="https://web.dev/patterns" track-metadata-eventdetail="https://web.dev/patterns" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - patterns" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Patterns" track-name="patterns" > Patterns </a> </tab> <tab > <a href="https://web.dev/case-studies" track-metadata-eventdetail="https://web.dev/case-studies" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - case studies" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Case studies" track-name="case studies" > Case studies </a> </tab> </nav> </devsite-tabs> </div> <devsite-search enable-signin enable-search enable-suggestions enable-query-completion project-name="Articles" tenant-name="web.dev" > <form class="devsite-search-form" action="https://web.dev/s/results" method="GET"> <div class="devsite-search-container"> <button type="button" search-open class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Open search"></button> <div class="devsite-searchbox"> <input aria-activedescendant="" aria-autocomplete="list" aria-label="Search" aria-expanded="false" aria-haspopup="listbox" autocomplete="off" class="devsite-search-field devsite-search-query" name="q" placeholder="Search" role="combobox" type="text" value="" > <div class="devsite-search-image material-icons" aria-hidden="true"> </div> <div class="devsite-search-shortcut-icon-container" aria-hidden="true"> <kbd class="devsite-search-shortcut-icon">/</kbd> </div> </div> </div> </form> <button type="button" search-close class="devsite-search-button devsite-header-icon-button button-flat material-icons" aria-label="Close search"></button> </devsite-search> </div> <devsite-appearance-selector></devsite-appearance-selector> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es" >Español</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> <devsite-user enable-profiles id="devsite-user"> <span class="button devsite-top-button" aria-hidden="true" visually-hidden>Sign in</span> </devsite-user> </div> </div> </div> <div class="devsite-collapsible-section "> <div class="devsite-header-background"> <div class="devsite-product-id-row" > <div class="devsite-product-description-row"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> <a href="https://web.dev/explore" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Lower Header" data-value="1" track-type="globalNav" track-name="breadcrumb" track-metadata-position="1" track-metadata-eventdetail="" > Collections </a> </li> </ul> </div> </div> <div class="devsite-doc-set-nav-row"> <devsite-tabs class="lower-tabs"> <nav class="devsite-tabs-wrapper" aria-label="Lower tabs"> <tab > <a href="https://web.dev/explore/learn-core-web-vitals" track-metadata-eventdetail="https://web.dev/explore/learn-core-web-vitals" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - core web vitals" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Core Web Vitals" track-name="core web vitals" > Core Web Vitals </a> </tab> <tab > <a href="https://web.dev/explore/metrics" track-metadata-eventdetail="https://web.dev/explore/metrics" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - metrics" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Metrics" track-name="metrics" > Metrics </a> </tab> <tab > <a href="https://web.dev/explore/fast" track-metadata-eventdetail="https://web.dev/explore/fast" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - fast load times" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Fast load times" track-name="fast load times" > Fast load times </a> </tab> <tab > <a href="https://web.dev/explore/ai" track-metadata-eventdetail="https://web.dev/explore/ai" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - ai" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: AI" track-name="ai" > AI </a> </tab> <tab > <a href="https://web.dev/explore/how-to-optimize-inp" track-metadata-eventdetail="https://web.dev/explore/how-to-optimize-inp" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - optimize interaction to next paint (inp)" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Optimize Interaction to Next Paint (INP)" track-name="optimize interaction to next paint (inp)" > Optimize Interaction to Next Paint (INP) </a> </tab> <tab > <a href="https://web.dev/explore/progressive-web-apps" track-metadata-eventdetail="https://web.dev/explore/progressive-web-apps" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - progressive web apps" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Progressive Web Apps" track-name="progressive web apps" > Progressive Web Apps </a> </tab> <tab > <a href="https://web.dev/accessibility" track-metadata-eventdetail="https://web.dev/accessibility" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - accessible to all" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Accessible to all" track-name="accessible to all" > Accessible to all </a> </tab> <tab > <a href="https://web.dev/explore/reliable" track-metadata-eventdetail="https://web.dev/explore/reliable" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - network reliability" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Network reliability" track-name="network reliability" > Network reliability </a> </tab> <tab > <a href="https://web.dev/explore/secure" track-metadata-eventdetail="https://web.dev/explore/secure" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - safe and secure" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Safe and secure" track-name="safe and secure" > Safe and secure </a> </tab> <tab > <a href="https://web.dev/explore/discoverable" track-metadata-eventdetail="https://web.dev/explore/discoverable" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - easily discoverable" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Easily discoverable" track-name="easily discoverable" > Easily discoverable </a> </tab> <tab > <a href="https://web.dev/explore/payments" track-metadata-eventdetail="https://web.dev/explore/payments" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - web payments" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Web Payments" track-name="web payments" > Web Payments </a> </tab> <tab > <a href="https://web.dev/explore/media" track-metadata-eventdetail="https://web.dev/explore/media" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - media" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Media" track-name="media" > Media </a> </tab> <tab > <a href="https://web.dev/explore/devices" track-metadata-eventdetail="https://web.dev/explore/devices" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - devices" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Devices" track-name="devices" > Devices </a> </tab> <tab > <a href="https://web.dev/explore/animations" track-metadata-eventdetail="https://web.dev/explore/animations" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - animations" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Animations" track-name="animations" > Animations </a> </tab> <tab class="devsite-active"> <a href="https://web.dev/explore/identity" track-metadata-eventdetail="https://web.dev/explore/identity" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - identity" track-metadata-module="primary nav" aria-label="Identity, selected" data-category="Site-Wide Custom Events" data-label="Tab: Identity" track-name="identity" > Identity </a> </tab> <tab > <a href="https://web.dev/explore/webassembly" track-metadata-eventdetail="https://web.dev/explore/webassembly" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - webassembly" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: WebAssembly" track-name="webassembly" > WebAssembly </a> </tab> <tab > <a href="https://web.dev/explore/test-automation" track-metadata-eventdetail="https://web.dev/explore/test-automation" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - test automation" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Test automation" track-name="test automation" > Test automation </a> </tab> <tab > <a href="https://web.dev/explore/react" track-metadata-eventdetail="https://web.dev/explore/react" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - react" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: React" track-name="react" > React </a> </tab> <tab > <a href="https://web.dev/explore/angular" track-metadata-eventdetail="https://web.dev/explore/angular" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - angular" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Angular" track-name="angular" > Angular </a> </tab> <tab > <a href="https://web.dev/explore/mini-apps" track-metadata-eventdetail="https://web.dev/explore/mini-apps" class="devsite-tabs-content gc-analytics-event " track-type="nav" track-metadata-position="nav - mini apps" track-metadata-module="primary nav" data-category="Site-Wide Custom Events" data-label="Tab: Mini apps" track-name="mini apps" > Mini apps </a> </tab> </nav> </devsite-tabs> </div> </div> </div> </div> </devsite-header> <devsite-book-nav scrollbars > <div class="devsite-book-nav-filter" hidden> <span class="filter-list-icon material-icons" aria-hidden="true"></span> <input type="text" placeholder="Filter" aria-label="Type to filter" role="searchbox"> <span class="filter-clear-button hidden" data-title="Clear filter" aria-label="Clear filter" role="button" tabindex="0"></span> </div> <nav class="devsite-book-nav devsite-nav nocontent" aria-label="Side menu"> <div class="devsite-mobile-header"> <button type="button" id="devsite-close-nav" class="devsite-header-icon-button button-flat material-icons gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Close navigation" aria-label="Close navigation"> </button> <div class="devsite-product-name-wrapper"> <a href="/" class="devsite-site-logo-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Site logo" track-type="globalNav" track-name="webDev" track-metadata-position="nav" track-metadata-eventDetail="nav"> <picture> <source srcset="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup-dark-theme.svg" media="(prefers-color-scheme: dark)" class="devsite-dark-theme" alt="web.dev"> <img src="https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup.svg" class="devsite-site-logo" alt="web.dev"> </picture> </a> <span class="devsite-product-name"> <ul class="devsite-breadcrumb-list" > <li class="devsite-breadcrumb-item "> </li> </ul> </span> </div> </div> <div class="devsite-book-nav-wrapper"> <div class="devsite-mobile-nav-top"> <ul class="devsite-nav-list"> <li class="devsite-nav-item"> <a href="/about" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: About" track-name="about" data-category="Site-Wide Custom Events" data-label="Responsive Tab: About" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > About </span> </a> </li> <li class="devsite-nav-item"> <a href="/html" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: HTML" track-name="html" data-category="Site-Wide Custom Events" data-label="Responsive Tab: HTML" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > HTML </span> </a> </li> <li class="devsite-nav-item"> <a href="/css" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: CSS" track-name="css" data-category="Site-Wide Custom Events" data-label="Responsive Tab: CSS" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > CSS </span> </a> </li> <li class="devsite-nav-item"> <a href="/javascript" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: JavaScript" track-name="javascript" data-category="Site-Wide Custom Events" data-label="Responsive Tab: JavaScript" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > JavaScript </span> </a> </li> <li class="devsite-nav-item"> <a href="/blog" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Blog" track-name="blog" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Blog" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Blog </span> </a> </li> <li class="devsite-nav-item"> <a href="/learn" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Learn" track-name="learn" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Learn" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Learn </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore" class="devsite-nav-title gc-analytics-event devsite-nav-active" data-category="Site-Wide Custom Events" data-label="Tab: Explore" track-name="explore" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Explore" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Explore </span> </a> <ul class="devsite-nav-responsive-tabs"> <li class="devsite-nav-item"> <a href="/explore/learn-core-web-vitals" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Core Web Vitals" track-name="core web vitals" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Core Web Vitals" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Core Web Vitals </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/metrics" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Metrics" track-name="metrics" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Metrics" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Metrics </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/fast" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Fast load times" track-name="fast load times" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Fast load times" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Fast load times </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/ai" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: AI" track-name="ai" data-category="Site-Wide Custom Events" data-label="Responsive Tab: AI" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > AI </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/how-to-optimize-inp" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Optimize Interaction to Next Paint (INP)" track-name="optimize interaction to next paint (inp)" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Optimize Interaction to Next Paint (INP)" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Optimize Interaction to Next Paint (INP) </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/progressive-web-apps" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Progressive Web Apps" track-name="progressive web apps" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Progressive Web Apps" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Progressive Web Apps </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/accessibility" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Accessible to all" track-name="accessible to all" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Accessible to all" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Accessible to all </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/reliable" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Network reliability" track-name="network reliability" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Network reliability" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Network reliability </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/secure" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Safe and secure" track-name="safe and secure" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Safe and secure" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Safe and secure </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/discoverable" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Easily discoverable" track-name="easily discoverable" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Easily discoverable" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Easily discoverable </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/payments" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Web Payments" track-name="web payments" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Web Payments" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Web Payments </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/media" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Media" track-name="media" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Media" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Media </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/devices" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Devices" track-name="devices" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Devices" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Devices </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/animations" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Animations" track-name="animations" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Animations" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Animations </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/identity" class="devsite-nav-title gc-analytics-event devsite-nav-has-children devsite-nav-active" data-category="Site-Wide Custom Events" data-label="Tab: Identity" track-name="identity" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Identity" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip menu="_book"> Identity </span> <span class="devsite-nav-icon material-icons" data-icon="forward" menu="_book"> </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/webassembly" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: WebAssembly" track-name="webassembly" data-category="Site-Wide Custom Events" data-label="Responsive Tab: WebAssembly" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > WebAssembly </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/test-automation" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Test automation" track-name="test automation" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Test automation" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Test automation </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/react" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: React" track-name="react" data-category="Site-Wide Custom Events" data-label="Responsive Tab: React" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > React </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/angular" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Angular" track-name="angular" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Angular" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Angular </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> <li class="devsite-nav-item"> <a href="/explore/mini-apps" class="devsite-nav-title gc-analytics-event devsite-nav-has-children " data-category="Site-Wide Custom Events" data-label="Tab: Mini apps" track-name="mini apps" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Mini apps" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Mini apps </span> <span class="devsite-nav-icon material-icons" data-icon="forward" > </span> </a> </li> </ul> </li> <li class="devsite-nav-item"> <a href="/patterns" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Patterns" track-name="patterns" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Patterns" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Patterns </span> </a> </li> <li class="devsite-nav-item"> <a href="/case-studies" class="devsite-nav-title gc-analytics-event " data-category="Site-Wide Custom Events" data-label="Tab: Case studies" track-name="case studies" data-category="Site-Wide Custom Events" data-label="Responsive Tab: Case studies" track-type="globalNav" track-metadata-eventDetail="globalMenu" track-metadata-position="nav"> <span class="devsite-nav-text" tooltip > Case studies </span> </a> </li> </ul> </div> <div class="devsite-mobile-nav-bottom"> <ul class="devsite-nav-list" menu="_book"> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Understand authentication basics</span> </div></li> <li class="devsite-nav-item"><a href="/articles/sign-in-form-best-practices" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/sign-in-form-best-practices" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/sign-in-form-best-practices" ><span class="devsite-nav-text" tooltip>Sign-in form best practices</span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://web.dev/codelab-sign-in-form-best-practices/" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://web.dev/codelab-sign-in-form-best-practices/" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://web.dev/codelab-sign-in-form-best-practices/" ><span class="devsite-nav-text" tooltip>[Codelab] Use cross-platform browser features to build a sign-in form</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item"><a href="/articles/sign-up-form-best-practices" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/sign-up-form-best-practices" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/sign-up-form-best-practices" ><span class="devsite-nav-text" tooltip>Sign-up form best practices</span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://web.dev/codelab-sign-up-form-best-practices/" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://web.dev/codelab-sign-up-form-best-practices/" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://web.dev/codelab-sign-up-form-best-practices/" ><span class="devsite-nav-text" tooltip>[Codelab] Sign-up form best practices codelab</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item"><a href="/articles/sign-out-best-practices" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/sign-out-best-practices" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/sign-out-best-practices" ><span class="devsite-nav-text" tooltip>What makes for a good sign-out experience?</span></a></li> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Build authentication systems with modern APIs</span> </div></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developer.chrome.com/docs/identity/web-apis/web-otp" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developer.chrome.com/docs/identity/web-apis/web-otp" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developer.chrome.com/docs/identity/web-apis/web-otp" ><span class="devsite-nav-text" tooltip>Verify phone numbers on the web with the WebOTP API</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item"><a href="/articles/change-password-url" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/change-password-url" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/change-password-url" ><span class="devsite-nav-text" tooltip>Help users change passwords easily by adding a well-known URL for changing passwords</span></a></li> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Strengthen security with two-factor authentication</span> </div></li> <li class="devsite-nav-item"><a href="/sms-otp-form" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /sms-otp-form" track-type="bookNav" track-name="click" track-metadata-eventdetail="/sms-otp-form" ><span class="devsite-nav-text" tooltip>SMS OTP form for two-step verification</span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/codelabs/webauthn-2fa-key/" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/codelabs/webauthn-2fa-key/" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/codelabs/webauthn-2fa-key/" ><span class="devsite-nav-text" tooltip>[Codelab] Enable two-factor authentication with WebAuthn</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Build advanced authentication systems</span> </div></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/identity/passkeys/" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/identity/passkeys/" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/identity/passkeys/" ><span class="devsite-nav-text" tooltip>Passwordless login with passkeys</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item"><a href="/articles/passkey-registration" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/passkey-registration" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/passkey-registration" ><span class="devsite-nav-text" tooltip>Create a passkey for passwordless logins</span></a></li> <li class="devsite-nav-item"><a href="/articles/passkey-form-autofill" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/passkey-form-autofill" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/passkey-form-autofill" ><span class="devsite-nav-text" tooltip>Sign in with a passkey through form autofill</span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/identity/passkeys/developer-guides/server-introduction" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/identity/passkeys/developer-guides/server-introduction" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/identity/passkeys/developer-guides/server-introduction" ><span class="devsite-nav-text" tooltip>Passkey server-side introduction</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/identity/passkeys/developer-guides/server-registration" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/identity/passkeys/developer-guides/server-registration" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/identity/passkeys/developer-guides/server-registration" ><span class="devsite-nav-text" tooltip>Passkey registration server-side </span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/identity/passkeys/developer-guides/server-authentication" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/identity/passkeys/developer-guides/server-authentication" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/identity/passkeys/developer-guides/server-authentication" ><span class="devsite-nav-text" tooltip>Passkey authentication server-side</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item"><a href="/articles/webauthn-user-verification" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/webauthn-user-verification" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/webauthn-user-verification" ><span class="devsite-nav-text" tooltip>User verification deep dive</span></a></li> <li class="devsite-nav-item"><a href="/articles/webauthn-discoverable-credentials" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/webauthn-discoverable-credentials" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/webauthn-discoverable-credentials" ><span class="devsite-nav-text" tooltip>Discoverable credentials deep dive</span></a></li> <li class="devsite-nav-item"><a href="/articles/webauthn-exclude-credentials" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/webauthn-exclude-credentials" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/webauthn-exclude-credentials" ><span class="devsite-nav-text" tooltip>Prevent duplicate credentials</span></a></li> <li class="devsite-nav-item"><a href="/articles/webauthn-aaguid" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/webauthn-aaguid" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/webauthn-aaguid" ><span class="devsite-nav-text" tooltip>Determine the passkey provider</span></a></li> <li class="devsite-nav-item"><a href="/articles/webauthn-related-origin-requests" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/webauthn-related-origin-requests" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/webauthn-related-origin-requests" ><span class="devsite-nav-text" tooltip>Associate related origins</span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developer.chrome.com/docs/identity/webauthn-signal-api" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developer.chrome.com/docs/identity/webauthn-signal-api" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developer.chrome.com/docs/identity/webauthn-signal-api" ><span class="devsite-nav-text" tooltip>Keep passkeys consistent with the server</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/identity/passkeys/developer-guides/upgrades" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/identity/passkeys/developer-guides/upgrades" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/identity/passkeys/developer-guides/upgrades" ><span class="devsite-nav-text" tooltip>Passkey upgrades</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/codelabs/passkey-form-autofill/" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/codelabs/passkey-form-autofill/" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/codelabs/passkey-form-autofill/" ><span class="devsite-nav-text" tooltip>[Codelab] Implement passkeys with form autofill</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/codelabs/webauthn-reauth/" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/codelabs/webauthn-reauth/" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/codelabs/webauthn-reauth/" ><span class="devsite-nav-text" tooltip>[Codelab] Set up a re-authentication functionality with WebAuthn</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Build authentication with identity federation</span> </div></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/identity/protocols/oauth2" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/identity/protocols/oauth2" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/identity/protocols/oauth2" ><span class="devsite-nav-text" tooltip>Use OAuth 2.0 to access Google APIs</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item devsite-nav-external"><a href="https://developers.google.com/identity/protocols/oauth2/openid-connect" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: https://developers.google.com/identity/protocols/oauth2/openid-connect" track-type="bookNav" track-name="click" track-metadata-eventdetail="https://developers.google.com/identity/protocols/oauth2/openid-connect" ><span class="devsite-nav-text" tooltip>Set up OpenID Connect compliant OAuth 2.0</span><span class="devsite-nav-icon material-icons" data-icon="external" data-title="External" aria-hidden="true"></span></a></li> <li class="devsite-nav-item devsite-nav-heading"><div class="devsite-nav-title devsite-nav-title-no-path"> <span class="devsite-nav-text" tooltip>Case studies</span> </div></li> <li class="devsite-nav-item"><a href="/articles/yahoo-japan-identity-case-study" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /articles/yahoo-japan-identity-case-study" track-type="bookNav" track-name="click" track-metadata-eventdetail="/articles/yahoo-japan-identity-case-study" ><span class="devsite-nav-text" tooltip>Yahoo! JAPAN&#39;s password-free authentication reduced inquiries by 25%, sped up sign-in time by 2.6x</span></a></li> <li class="devsite-nav-item"><a href="/goibibo#web-otp" class="devsite-nav-title gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Book nav link, pathname: /goibibo#web-otp" track-type="bookNav" track-name="click" track-metadata-eventdetail="/goibibo#web-otp" ><span class="devsite-nav-text" tooltip>Goibibo dropped OTP retry on sign-up by 25% with WebOTP</span></a></li> </ul> </div> </div> </nav> </devsite-book-nav> <section id="gc-wrapper"> <main role="main" class="devsite-main-content" has-book-nav has-sidebar > <div class="devsite-sidebar"> <div class="devsite-sidebar-content"> <devsite-toc class="devsite-nav" role="navigation" aria-label="On this page" depth="2" scrollbars ></devsite-toc> <devsite-recommendations-sidebar class="nocontent devsite-nav"> </devsite-recommendations-sidebar> </div> </div> <devsite-content> <article class="devsite-article"> <div class="devsite-article-meta nocontent" role="navigation"> <ul class="devsite-breadcrumb-list" aria-label="Breadcrumb"> <li class="devsite-breadcrumb-item "> <a href="https://web.dev/" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="1" track-type="globalNav" track-name="breadcrumb" track-metadata-position="1" track-metadata-eventdetail="" > Home </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://web.dev/articles" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="2" track-type="globalNav" track-name="breadcrumb" track-metadata-position="2" track-metadata-eventdetail="Articles" > Articles </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://web.dev/explore" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="3" track-type="globalNav" track-name="breadcrumb" track-metadata-position="3" track-metadata-eventdetail="" > Explore </a> </li> <li class="devsite-breadcrumb-item "> <div class="devsite-breadcrumb-guillemet material-icons" aria-hidden="true"></div> <a href="https://web.dev/explore/identity" class="devsite-breadcrumb-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Breadcrumbs" data-value="4" track-type="globalNav" track-name="breadcrumb" track-metadata-position="4" track-metadata-eventdetail="" > Identity </a> </li> </ul> <devsite-thumb-rating position="header"> </devsite-thumb-rating> </div> <h1 class="devsite-page-title" tabindex="-1"> Sign-in form best practices </h1> <devsite-feature-tooltip ack-key="AckCollectionsBookmarkTooltipDismiss" analytics-category="Site-Wide Custom Events" analytics-action-show="Callout Profile displayed" analytics-action-close="Callout Profile dismissed" analytics-label="Create Collection Callout" class="devsite-page-bookmark-tooltip nocontent" dismiss-button="true" id="devsite-collections-dropdown" dismiss-button-text="Dismiss" close-button-text="Got it"> <devsite-bookmark></devsite-bookmark> <span slot="popout-heading"> Stay organized with collections </span> <span slot="popout-contents"> Save and categorize content based on your preferences. </span> </devsite-feature-tooltip> <div class="devsite-page-title-meta"><devsite-view-release-notes></devsite-view-release-notes></div> <devsite-toc class="devsite-nav" depth="2" devsite-toc-embedded > </devsite-toc> <div class="devsite-article-body clearfix "> <p>Use cross-platform browser features to build sign-in forms that are secure, accessible and easy to use.</p> <p> <div class="wd-authors" translate="no"> <div class="wd-author"> <img class="devsite-landing-row-item-icon" alt="Sam Dutton" src="https://web.dev/images/authors/samdutton.jpg" decoding="async" height="64" loading="lazy" width="64"> <div> <span> Sam Dutton </span> <div class="wd-author__links"> <a href="https://twitter.com/sw12" aria-label="Sam Dutton on X" rel="me"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 300 271"> <title>X</title> <path fill="currentColor" d="m236 0h46l-101 115 118 156h-92.6l-72.5-94.8-83 94.8h-46l107-123-113-148h94.9l65.5 86.6zm-16.1 244h25.5l-165-218h-27.4z"></path> </svg></a> <a href="https://github.com/samdutton" aria-label="Sam Dutton on GitHub" rel="me"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 32.6 31.8"> <title>GitHub</title> <path d="M16.3 0C7.3 0 0 7.3 0 16.3c0 7.2 4.7 13.3 11.1 15.5.8.1 1.1-.4 1.1-.8v-2.8c-4.5 1-5.5-2.2-5.5-2.2-.7-1.9-1.8-2.4-1.8-2.4-1.5-1 .1-1 .1-1 1.6.1 2.5 1.7 2.5 1.7 1.5 2.5 3.8 1.8 4.7 1.4.1-1.1.6-1.8 1-2.2-3.6-.4-7.4-1.8-7.4-8.1 0-1.8.6-3.2 1.7-4.4-.1-.3-.7-2 .2-4.2 0 0 1.4-.4 4.5 1.7 1.3-.4 2.7-.5 4.1-.5 1.4 0 2.8.2 4.1.5 3.1-2.1 4.5-1.7 4.5-1.7.9 2.2.3 3.9.2 4.3 1 1.1 1.7 2.6 1.7 4.4 0 6.3-3.8 7.6-7.4 8 .6.5 1.1 1.5 1.1 3V31c0 .4.3.9 1.1.8 6.5-2.2 11.1-8.3 11.1-15.5C32.6 7.3 25.3 0 16.3 0z" fill-rule="evenodd" clip-rule="evenodd" fill="currentColor" /> </svg></a> <a href="https://glitch.com/@samdutton" aria-label="Sam Dutton on Glitch" rel="me"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 32 32"> <title>Glitch</title> <path fill="currentColor" d="M31.734 16.76c-.385-.198-4.536 1.865-5.427 1.693-2.24-.401-1.828-.667-4.839-1.359-1.203-.266-1.031-.109-1.297-.307-.172-.135-.344-.161-.599-.401 4-.719 6.026-1.693 6.734-1.839.76-.146 5.161 1.958 5.427 1.469.266-.495-.964-1.578-.401-3.031.589-1.464-.693-2.422.016-3.583.719-1.161.573-2.932.396-3.026-.396-.203-4.531 1.865-5.438 1.693-2.24-.417-1.828-.682-4.839-1.359-1.203-.271-1.031-.12-1.297-.323-.266-.198-.521-.13-1.036-.974-.521-.839-6.51-2.13-6.906-2.13-.828 0-2.375 2.13-2.375 2.13s-.599 0-2.401.094c-1.802.094-3.375.896-5.495 2.563C-.173 9.737.134 11.414.134 11.414s1.969.667 1.969 1.042c0 .359-1.729.802-1.729.802 1.12 1.411 4.583 2.745 5.464 2.745h.693c-1.438.281-2.823 1.068-4.583 2.438-2.12 1.698-1.813 3.375-1.813 3.375s1.969.667 1.969 1.026-1.729.802-1.729.802c1.12 1.427 4.583 2.76 5.464 2.76.844 0 1.427.026 2.495-.172.078.172.906 1.932 2.599 2.292 1.786.385 2.776.078 2.776.078s.094-.786-.323-1.573c1.547.161 3.307.203 5.026-.068 4.76-.719 7.12-1.865 7.896-2.01.76-.161 5.161 1.948 5.427 1.464.266-.505-.964-1.583-.385-3.036.573-1.469-.708-2.417 0-3.589.719-1.161.573-2.932.396-3.026zM4.615 11.828a1.446 1.446 0 0 1-.297-.042h-.052c-.026-.01-.052-.026-.078-.042l-.052-.01-.083-.042h-.052a.418.418 0 0 1-.068-.042l-.068-.052-.063-.036-.057-.042c-.021-.016-.042-.036-.063-.052l-.042-.042c-.026-.026-.047-.052-.068-.078l-.026-.031a1.954 1.954 0 0 1-.094-.104l-.026-.026c-.021-.036-.036-.073-.052-.109l-.026-.036-.057-.083c-.005-.021-.016-.042-.026-.063l-.026-.083-.026-.052-.016-.094-.01-.068c-.01-.026-.021-.052-.026-.078v-.068c.094.573.557 1.016 1.104 1.016.63 0 1.146-.573 1.146-1.297 0-.719-.505-1.307-1.146-1.307-.625 0-1.13.573-1.146 1.281 0-.932.667-1.693 1.495-1.693.823 0 1.479.745 1.479 1.682 0 .932-.667 1.693-1.479 1.693zm-1-1.265c0-.203.13-.365.318-.365s.307.161.307.365c0 .198-.135.344-.307.344s-.318-.161-.318-.344zm1 11.651a.712.712 0 0 1-.146 0l-.057-.016a.6.6 0 0 1-.094-.01l-.052-.016-.078-.026-.052-.026c-.031-.005-.057-.016-.083-.026l-.052-.026c-.021-.016-.047-.026-.068-.042L3.881 22l-.068-.052-.052-.042-.068-.052-.042-.042c-.031-.031-.063-.057-.089-.094a.671.671 0 0 1-.094-.12l-.031-.026c-.016-.031-.036-.063-.052-.094l-.026-.052c-.016-.026-.036-.052-.052-.078l-.026-.057-.026-.094-.026-.052-.031-.094-.01-.052c-.01-.031-.021-.063-.026-.094v-.068c.094.573.557 1.016 1.104 1.016.63 0 1.146-.573 1.146-1.292 0-.724-.505-1.297-1.146-1.297-.625 0-1.13.563-1.146 1.266 0-.932.667-1.693 1.495-1.693.823 0 1.479.76 1.479 1.682 0 .917-.667 1.693-1.479 1.693zm-1-1.266c0-.188.13-.349.318-.349s.307.161.307.349c0 .188-.135.344-.307.344s-.318-.146-.318-.344zm6.77-7.333v-.042l.042-.078.078-.297c.182-.583.344-1.172.479-1.771.161-.708.229-1.281.203-1.599-.016-.12-.031-.245-.052-.359a8.276 8.276 0 0 0-.521-1.724l-.083-.172-.026-.068c-.12-.266.057-.573.323-.557h.188l.531.036 2.104.109 1.151.078a28.24 28.24 0 0 1 10.573 2.828l.891.401c.172.078.266.307.188.505-.068.188-.266.292-.438.214l-.896-.401a27.695 27.695 0 0 0-10.359-2.786l-1.146-.068-.51-.026-1.599-.094h-.156c.188.51.339 1.031.453 1.562l.063.427c.042.453-.036 1.078-.224 1.88l-.203.823a23.62 23.62 0 0 1-.385 1.323l-.026.078v.042c-.068.188-.266.292-.438.214-.177-.068-.271-.292-.203-.495zm-2-6.349a.307.307 0 0 1 .479.026c.208.26.396.536.563.828.292.531.495 1.068.547 1.615.026.307 0 .651-.052 1.026a8.718 8.718 0 0 1-.271 1.104c-.094.313-.208.62-.333.922-.078.188-.276.266-.453.172-.172-.094-.24-.318-.156-.521l.026-.052.068-.172c.073-.198.146-.396.214-.599.099-.328.182-.661.24-1 .052-.307.063-.573.052-.802a3.47 3.47 0 0 0-.453-1.292 4.794 4.794 0 0 0-.443-.667l-.036-.042a.417.417 0 0 1 .026-.531zm1.537 13.869c-.063.38-.151.76-.271 1.13a9.549 9.549 0 0 1-.333.906c-.078.188-.276.266-.453.177-.172-.094-.24-.323-.156-.521l.026-.057.068-.172c.073-.198.146-.396.214-.599.099-.328.182-.661.24-1 .052-.307.063-.573.036-.802a3.365 3.365 0 0 0-.438-1.276 4.794 4.794 0 0 0-.443-.667l-.036-.057a.417.417 0 0 1 .026-.531.3.3 0 0 1 .464 0c.214.266.396.547.563.839.292.536.495 1.083.547 1.615.026.307 0 .651-.052 1.026zm16.531.157c-.068.188-.266.297-.438.214l-.896-.401a27.695 27.695 0 0 0-10.359-2.786l-1.135-.063h-.063l-.458-.026c-.583-.036-1.172-.068-1.755-.094l.036.078c.234.615.396 1.255.479 1.906.042.453-.036 1.078-.224 1.88l-.203.828a24.99 24.99 0 0 1-.385 1.333l-.026.068v.036c-.068.203-.266.297-.438.229a.42.42 0 0 1-.203-.51v-.026l.042-.078.078-.292c.182-.589.344-1.177.479-1.776.161-.708.229-1.281.203-1.599-.016-.12-.031-.24-.052-.359a7.996 7.996 0 0 0-.521-1.708l-.052-.12-.031-.068-.026-.063c-.12-.271.057-.578.323-.563h.188l.531.042 2.12.104 1.135.083a28.14 28.14 0 0 1 10.573 2.823l.891.401c.172.078.266.307.188.505z"/> </svg></a> <a href="https://techhub.social/@samdutton" aria-label="Sam Dutton on Mastodon" rel="me"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 16 16"> <title>Mastodon</title> <path fill="currentColor" d="M 15.659 9.592 C 15.424 10.72 13.553 11.956 11.404 12.195 C 10.283 12.32 9.18 12.434 8.003 12.384 C 6.079 12.302 4.56 11.956 4.56 11.956 C 4.56 12.13 4.572 12.297 4.595 12.452 C 4.845 14.224 6.478 14.33 8.025 14.379 C 9.586 14.429 10.976 14.02 10.976 14.02 L 11.04 15.337 C 11.04 15.337 9.948 15.884 8.003 15.984 C 6.93 16.039 5.598 15.959 4.047 15.576 C 0.683 14.746 0.104 11.4 0.015 8.006 C -0.012 6.998 0.005 6.048 0.005 5.253 C 0.005 1.782 2.443 0.765 2.443 0.765 C 3.672 0.238 5.782 0.017 7.975 0 L 8.029 0 C 10.221 0.017 12.332 0.238 13.561 0.765 C 13.561 0.765 15.999 1.782 15.999 5.253 C 15.999 5.253 16.03 7.814 15.659 9.592 Z M 13.124 5.522 L 13.124 9.725 L 11.339 9.725 L 11.339 5.646 C 11.339 4.786 10.951 4.35 10.175 4.35 C 9.317 4.35 8.887 4.867 8.887 5.891 L 8.887 8.124 L 7.113 8.124 L 7.113 5.891 C 7.113 4.867 6.683 4.35 5.825 4.35 C 5.049 4.35 4.661 4.786 4.661 5.646 L 4.661 9.725 L 2.876 9.725 L 2.876 5.522 C 2.876 4.663 3.111 3.981 3.582 3.476 C 4.067 2.971 4.703 2.712 5.493 2.712 C 6.406 2.712 7.098 3.039 7.555 3.695 L 8 4.39 L 8.445 3.695 C 8.902 3.039 9.594 2.712 10.507 2.712 C 11.297 2.712 11.933 2.971 12.418 3.476 C 12.889 3.981 13.124 4.663 13.124 5.522 Z" style="stroke:none;stroke-miterlimit:10;fill-rule:evenodd;"></path> </svg></a> <a href="https://samdutton.com" aria-label="Sam Dutton's homepage" rel="me"> <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 30 30"> <title>Homepage</title> <circle cx="14.5" cy="14.5" r="13.5" stroke-width="2" stroke-miterlimit="10" fill="none" stroke="currentColor" /> <ellipse cx="14.5" cy="14.5" rx="6.1" ry="13.5" stroke-width="2" stroke-miterlimit="10" fill="none" stroke="currentColor" /> <path d="M1.6 9.6h25.8M1.6 19.4h25.8" stroke-width="2" stroke-miterlimit="10" fill="none" stroke="currentColor" /> </svg></a> </div> </div> </div> </div></p> <p><devsite-video video-id="alGcULGtiv8"></devsite-video></p> <p>If users ever need to log in to your site, then good sign-in form design is critical. This is especially true for people on poor connections, on mobile, in a hurry, or under stress. Poorly designed sign-in forms get high bounce rates. Each bounce could mean a lost and disgruntled user—not just a missed sign-in opportunity.</p> <aside class="beta"><b>Try it: </b> If you would prefer to learn these best practices with a hands-on tutorial, check out the <a href="/articles/codelab-sign-in-form-best-practices">Sign-in form best practices codelab</a>. </aside> <p>Here is an example of a simple sign-in form that demonstrates all of the best practices:</p> <div class="wd-embed" style="height: 480px;"> <iframe allow="camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi" loading="lazy" src="https://glitch.com/embed/#!/embed/sign-in-form?attributionHidden=true&sidebarCollapsed=true&path=index.html&previewSize=100" style="height: 100%; width: 100%; border: 0;" title="sign-in-form on Glitch" ></iframe> </div> <h2 id="checklist" data-text="Checklist" tabindex="-1">Checklist</h2> <ul> <li><a href="#meaningful-html">Use meaningful HTML elements</a>: <code translate="no" dir="ltr">&lt;form&gt;</code>, <code translate="no" dir="ltr">&lt;input&gt;</code>, <code translate="no" dir="ltr">&lt;label&gt;</code>, and <code translate="no" dir="ltr">&lt;button&gt;</code>.</li> <li><a href="#label">Label each input with a <code translate="no" dir="ltr">&lt;label&gt;</code></a>.</li> <li>Use element attributes to <a href="#element-attributes">access built-in browser features</a>: <code translate="no" dir="ltr">type</code>, <code translate="no" dir="ltr">name</code>, <code translate="no" dir="ltr">autocomplete</code>, <code translate="no" dir="ltr">required</code>.</li> <li>Give input <code translate="no" dir="ltr">name</code> and <code translate="no" dir="ltr">id</code> attributes stable values that don&#39;t change between page loads or website deployments.</li> <li>Put sign-in <a href="#form">in its own &lt;form&gt; element</a>.</li> <li><a href="#submission">Ensure successful form submission</a>.</li> <li>Use <a href="#new-password"><code translate="no" dir="ltr">autocomplete=&quot;new-password&quot;</code></a> and <a href="#new-password"><code translate="no" dir="ltr">id=&quot;new-password&quot;</code></a> for the password input in a sign-up form, and for the new password in a reset-password form.</li> <li>Use <a href="#current-password"><code translate="no" dir="ltr">autocomplete=&quot;current-password&quot;</code></a> and <a href="#current-password"><code translate="no" dir="ltr">id=&quot;current-password&quot;</code></a> for a sign-in password input.</li> <li>Provide <a href="#show-password">Show password</a> functionality.</li> <li><a href="#accessible-password-inputs">Use <code translate="no" dir="ltr">aria-label</code> and <code translate="no" dir="ltr">aria-describedby</code></a> for password inputs.</li> <li><a href="#no-double-inputs">Don&#39;t double-up inputs</a>.</li> <li>Design forms so the <a href="#keyboard-obstruction">mobile keyboard doesn&#39;t obscure inputs or buttons</a>.</li> <li>Ensure forms are usable on mobile: use <a href="#size-text-correctly">legible text</a>, and make sure inputs and buttons are <a href="#tap-targets">large enough to work as touch targets</a>.</li> <li><a href="#general-guidelines">Maintain branding and style</a> on your sign-up and sign-in pages.</li> <li><a href="#analytics">Test in the field as well as the lab</a>: build page analytics, interaction analytics, and user-centric performance measurement into your sign-up and sign-in flow.</li> <li><a href="#devices">Test across browsers and devices</a>: form behaviour varies significantly across platforms.</li> </ul> <aside class="note"><b>Note: </b> This article is about frontend best practices. It does not explain how to build backend services to authenticate users, store their credentials, or manage their accounts. <a href="https://cloud.google.com/blog/products/gcp/12-best-practices-for-user-account">12 best practices for user account, authorization and password management</a> outlines core principles for running your own backend. If you have users in different parts of the world, you need to consider localizing your site's use of third-party identity services as well as its content. There are also two relatively new APIs not covered in this article which can help you build a better sign-in experience: * <a href="/web-otp"><strong>WebOTP</strong></a>: to deliver one-time passcodes or PIN numbers via SMS to mobile phones. This can allow users to select a phone number as an identifier (no need to enter an email address!) and also enables two-step verification for sign-in and one-time codes for payment confirmation. * <a href="https://developer.chrome.com/blog/credential-management-api"><strong>Credential Management</strong></a>: to enable developers to store and retrieve password credentials and federated credentials programmatically. </aside> <h2 id="meaningful-html" data-text="Use meaningful HTML" tabindex="-1">Use meaningful HTML</h2> <p>Use elements built for the job: <code translate="no" dir="ltr">&lt;form&gt;</code>, <code translate="no" dir="ltr">&lt;label&gt;</code> and <code translate="no" dir="ltr">&lt;button&gt;</code>. These enable built-in browser functionality, improve accessibility, and add meaning to your markup.</p> <h3 id="form" data-text="Use &lt;form&gt;" tabindex="-1">Use <code translate="no" dir="ltr">&lt;form&gt;</code></h3> <p>You might be tempted to wrap inputs in a <code translate="no" dir="ltr">&lt;div&gt;</code> and handle input data submission purely with JavaScript. It&#39;s generally better to use a plain old <a href="https://developer.mozilla.org/docs/Web/HTML/Element/form"><code translate="no" dir="ltr">&lt;form&gt;</code></a> element. This makes your site accessible to screenreaders and other assistive devices, enables a range of built-in browser features, makes it simpler to build basic functional sign-in for older browsers, and can still work even if JavaScript fails.</p> <aside class="tip"> A common mistake is to wrap a whole web page in a single form, but this is liable to cause problems for browser password managers and autofill. Use a different &lt;form&gt; for each UI component that needs a form. For example, if you have sign-in and search on the same page, you should use two form elements. </aside> <h3 id="label" data-text="Use &lt;label&gt;" tabindex="-1">Use <code translate="no" dir="ltr">&lt;label&gt;</code></h3> <p>To label an input, use a <a href="https://developer.mozilla.org/docs/Web/HTML/Element/label"><code translate="no" dir="ltr">&lt;label&gt;</code></a>!</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="HTML"><code translate="no" dir="ltr">&lt;label for="email"&gt;Email&lt;/label&gt; &lt;input id="email" …&gt; </code></pre></devsite-code> <p>Two reasons:</p> <ul> <li>A tap or click on a label moves focus to its input. Associate a label with an input by using the label&#39;s <code translate="no" dir="ltr">for</code> attribute with the input&#39;s <code translate="no" dir="ltr">name</code> or <code translate="no" dir="ltr">id</code>.</li> <li>Screenreaders announce label text when the label or the label&#39;s input gets focus.</li> </ul> <p>Don&#39;t use placeholders as input labels. People are liable to forget what the input was for once they&#39;ve started entering text, especially if they get distracted (&quot;Was I entering an email address, a phone number, or an account ID?&quot;). There are lots of other potential problems with placeholders: see <a href="https://www.smashingmagazine.com/2018/06/placeholder-attribute/">Don&#39;t Use The Placeholder Attribute</a> and <a href="https://www.nngroup.com/articles/form-design-placeholders/">Placeholders in Form Fields Are Harmful</a> if you&#39;re unconvinced.</p> <p>It&#39;s probably best to put your labels above your inputs. This enables consistent design across mobile and desktop and, according to <a href="https://ai.googleblog.com/2014/07/simple-is-better-making-your-web-forms.html">Google AI research</a>, enables quicker scanning by users. You get full width labels and inputs, and you don&#39;t need to adjust label and input width to fit the label text.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3.png" alt="Screenshot showing form input label position on mobile: next to input and above input." width="500" height="253" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-showing-form-i-8eaa2616f31e3_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Label and input width is limited when both are on the same line.</figcaption> </figure> <p>Open the <a href="https://label-position.glitch.me">label-position</a> Glitch on a mobile device to see for yourself.</p> <h3 id="button" data-text="Use &lt;button&gt;" tabindex="-1">Use <code translate="no" dir="ltr">&lt;button&gt;</code></h3> <p>Use <a href="https://developer.mozilla.org/docs/Web/HTML/Element/button"><code translate="no" dir="ltr">&lt;button&gt;</code></a> for buttons! Button elements provide accessible behaviour and built-in form submission functionality, and they can easily be styled. There&#39;s no point in using a <code translate="no" dir="ltr">&lt;div&gt;</code> or some other element pretending to be a button.</p> <p>Ensure that the submit button says what it does. Examples include <strong>Create account</strong> or <strong>Sign in</strong>, not <strong>Submit</strong> or <strong>Start</strong>.</p> <h3 id="submission" data-text="Ensure successful form submission" tabindex="-1">Ensure successful form submission</h3> <p>Help password managers understand that a form has been submitted. There are two ways to do that:</p> <ul> <li>Navigate to a different page.</li> <li>Emulate navigation with <code translate="no" dir="ltr">History.pushState()</code> or <code translate="no" dir="ltr">History.replaceState()</code>, and remove the password form.</li> </ul> <p>With an <code translate="no" dir="ltr">XMLHttpRequest</code> or <code translate="no" dir="ltr">fetch</code> request, make sure that sign-in success is reported in the response and handled by taking the form out of the DOM as well as indicating success to the user.</p> <p>Consider disabling the <strong>Sign in</strong> button once the user has tapped or clicked it. <a href="https://baymard.com/blog/users-double-click-online">Many users click buttons multiple times</a> even on sites that are fast and responsive. That slows down interactions and adds to server load.</p> <p>Conversely, don&#39;t disable form submission awaiting user input. For example, don&#39;t disable the <strong>Sign in</strong> button if users haven&#39;t entered their customer PIN. Users may miss out something in the form, then try repeatedly tapping the (disabled) <strong>Sign in</strong> button and think it&#39;s not working. At the very least, if you must disable form submission, explain to the user what&#39;s missing when they click on the disabled button.</p> <aside class="caution"><b>Caution: </b> The default type for a button in a form is <code translate="no" dir="ltr">submit</code>. If you want to add another button in a form (for <strong>Show password</strong>, for example) add <code translate="no" dir="ltr">type=&quot;button&quot;</code>. Otherwise clicking or tapping on the button will submit the form. Pressing the <code translate="no" dir="ltr">Enter</code> key while any form field has focus simulates a click on the first <code translate="no" dir="ltr">submit</code> button in the form. If you include a button in your form before the <strong>Submit</strong> button, and don't specify the type, that button will have the default type for buttons in a form (<code translate="no" dir="ltr">submit</code>) and receive the click event before the form is submitted. For an example of this, see our <a href="https://enter-button.glitch.me/">demo</a>: fill in the form, then press <code translate="no" dir="ltr">Enter</code>. </aside> <h3 id="no-double-inputs" data-text="Don't double up inputs" tabindex="-1">Don't double up inputs</h3> <p>Some sites force users to enter emails or passwords twice. That might reduce errors for a few users, but causes extra work for <em>all</em> users, and <a href="https://uxmovement.com/forms/why-the-confirm-password-field-must-die/">increases abandonment rates</a>. Asking twice also makes no sense where browsers autofill email addresses or suggest strong passwords. It&#39;s better to enable users to confirm their email address (you&#39;ll need to do that anyway) and make it easy for them to reset their password if necessary.</p> <h2 id="element-attributes" data-text="Make the most of element attributes" tabindex="-1">Make the most of element attributes</h2> <p>This is where the magic really happens! Browsers have multiple helpful built-in features that use input element attributes.</p> <h2 id="show-password" data-text="Keep passwords private—but enable users to see them if they want" tabindex="-1">Keep passwords private—but enable users to see them if they want</h2> <p>Passwords inputs should have <code translate="no" dir="ltr">type=&quot;password&quot;</code> to hide password text and help the browser understand that the input is for passwords. (Note that browsers use <a href="#autofill">a variety of techniques</a> to understand input roles and decide whether or not to offer to save passwords.)</p> <p>You should add a <strong>Show password</strong> toggle to enable users to check the text they&#39;ve entered—and don&#39;t forget to add a <strong>Forgot password</strong> link. See <a href="#password-display">Enable password display</a>.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685.png" alt="Google sign-in form showing Show password icon." width="300" height="107" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/google-sign-form-showing-c9486bea5e685_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Password input from the Google sign-in form: with <strong>Show password</strong> icon and <strong>Forgot password</strong> link.</figcaption> </figure> <h2 id="mobile-keyboards" data-text="Give mobile users the right keyboard" tabindex="-1">Give mobile users the right keyboard</h2> <p>Use <code translate="no" dir="ltr">&lt;input type=&quot;email&quot;&gt;</code> to give mobile users an appropriate keyboard and enable basic built-in email address validation by the browser… no JavaScript required!</p> <p>If you need to use a telephone number instead of an email address, <code translate="no" dir="ltr">&lt;input type=&quot;tel&quot;&gt;</code> enables a telephone keypad on mobile. You can also use the <code translate="no" dir="ltr">inputmode</code> attribute where necessary: <code translate="no" dir="ltr">inputmode=&quot;numeric&quot;</code> is ideal for PIN numbers. <a href="https://css-tricks.com/everything-you-ever-wanted-to-know-about-inputmode/">Everything You Ever Wanted to Know About inputmode</a> has more detail.</p> <aside class="caution"><b>Caution: </b> <code translate="no" dir="ltr">type=&quot;number&quot;</code> adds an up/down arrow to increment numbers, so don't use it for numbers that aren't meant to be incremented, such as IDs and account numbers. </aside> <h3 id="keyboard-obstruction" data-text="Prevent mobile keyboard from obstructing the Sign in button" tabindex="-1">Prevent mobile keyboard from obstructing the <strong>Sign in</strong> button</h3> <p>Unfortunately, if you&#39;re not careful, mobile keyboards may cover your form or, worse, partially obstruct the <strong>Sign in</strong> button. Users may give up before realizing what has happened.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027.png" alt="Two screenshots of a sign-in form on an Android phone: one showing how the Submit button is obscured by the phone keyboard." width="400" height="360" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/two-screenshots-a-sign-084751e480027_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">The <b>Sign in</b> button: now you see it, now you don't.</figcaption> </figure> <p>Where possible, avoid this by displaying only the email/phone and password inputs and <strong>Sign in</strong> button at the top of your sign-in page. Put other content below.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef.png" alt="Screenshot of a sign-in form on an Android phone: the Sign in button is not obscured by the phone keyboard." width="200" height="342" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-83485069857ef_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">The keyboard doesn't obstruct the <b>Sign in</b> button.</figcaption> </figure> <h4 id="devices" data-text="Test on a range of devices" tabindex="-1">Test on a range of devices</h4> <p>You&#39;ll need to test on a range of devices for your target audience, and adjust accordingly. BrowserStack enables <a href="https://www.browserstack.com/open-source">free testing for open source projects</a> on a range of real devices and browsers.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b.png" alt="Screenshots of a sign-in form on iPhone 7, 8 and 11. On iPhone 7 and 8 the Sign in button is obscured by the phone keyboard, but not on iPhone 11" width="800" height="522" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-a-sign-form-7ab582516302b_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">The <b>Sign in</b> button: obscured on iPhone 7 and 8, but not on iPhone 11.</figcaption> </figure> <h4 id="two-pages" data-text="Consider using two pages" tabindex="-1">Consider using two pages</h4> <p>Some sites (including Amazon and eBay) avoid the problem by asking for email/phone and password on two pages. This approach also simplifies the experience: the user is only tasked with one thing at a time.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904.png" alt="Screenshot of a sign-in form on the Amazon website: email/phone and password on two separate 'pages'." width="400" height="385" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-ade5c293b1904_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Two-stage sign-in: email or phone, then password.</figcaption> </figure> <p>Ideally, this should be implemented with a single &lt;form&gt;. Use JavaScript to initially display only the email input, then hide it and show the password input. If you must force the user to navigate to a new page between entering their email and password, the form on the second page should have a hidden input element with the email value, to help enable password managers to store the correct value. <a href="https://www.chromium.org/developers/design-documents/form-styles-that-chromium-understands">Password Form Styles that Chromium Understands</a> provides a code example.</p> <h3 id="autofill" data-text="Help users to avoid re-entering data" tabindex="-1">Help users to avoid re-entering data</h3> <p>You can help browsers store data correctly and autofill inputs, so users don&#39;t have to remember to enter email and password values. This is particularly important on mobile, and crucial for email inputs, which get <a href="https://www.formisimo.com/blog/conversion-rate-increases-57-with-form-analytics-case-study/">high abandonment rates</a>.</p> <p>There are two parts to this:</p> <ol> <li><p>The <code translate="no" dir="ltr">autocomplete</code>, <code translate="no" dir="ltr">name</code>, <code translate="no" dir="ltr">id</code>, and <code translate="no" dir="ltr">type</code> attributes help browsers understand the role of inputs in order to store data that can later be used for autofill. To allow data to be stored for autofill, modern browsers also require inputs to have a stable <code translate="no" dir="ltr">name</code> or <code translate="no" dir="ltr">id</code> value (not randomly generated on each page load or site deployment), and to be in a &lt;form&gt; with a <code translate="no" dir="ltr">submit</code> button.</p></li> <li><p>The <code translate="no" dir="ltr">autocomplete</code> attribute helps browsers correctly autofill inputs using stored data.</p></li> </ol> <p>For email inputs use <code translate="no" dir="ltr">autocomplete=&quot;username&quot;</code>, since <code translate="no" dir="ltr">username</code> is recognized by password managers in modern browsers—even though you should use <code translate="no" dir="ltr">type=&quot;email&quot;</code> and you may want to use <code translate="no" dir="ltr">id=&quot;email&quot;</code> and <code translate="no" dir="ltr">name=&quot;email&quot;</code>.</p> <p>For password inputs, use the appropriate <code translate="no" dir="ltr">autocomplete</code> and <code translate="no" dir="ltr">id</code> values to help browsers differentiate between new and current passwords.</p> <h3 id="new-password" data-text='Use autocomplete="new-password" and id="new-password" for a new password' tabindex="-1">Use <code translate="no" dir="ltr">autocomplete="new-password"</code> and <code translate="no" dir="ltr">id="new-password"</code> for a new password</h3> <ul> <li>Use <code translate="no" dir="ltr">autocomplete=&quot;new-password&quot;</code> and <code translate="no" dir="ltr">id=&quot;new-password&quot;</code> for the password input in a sign-up form, or the new password in a change-password form.</li> </ul> <h3 id="current-password" data-text='Use autocomplete="current-password" and id="current-password" for an existing password' tabindex="-1">Use <code translate="no" dir="ltr">autocomplete="current-password"</code> and <code translate="no" dir="ltr">id="current-password"</code> for an existing password</h3> <ul> <li>Use <code translate="no" dir="ltr">autocomplete=&quot;current-password&quot;</code> and <code translate="no" dir="ltr">id=&quot;current-password&quot;</code> for the password input in a sign-in form, or the input for the user&#39;s old password in a change-password form. This tells the browser that you want it to use the current password that it has stored for the site.</li> </ul> <p>For a sign-up form:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="HTML"><code translate="no" dir="ltr">&lt;input type="password" autocomplete="new-password" id="new-password" …&gt; </code></pre></devsite-code> <p>For sign-in:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="HTML"><code translate="no" dir="ltr">&lt;input type="password" autocomplete="current-password" id="current-password" …&gt; </code></pre></devsite-code> <aside class="note"><b>Note: </b> Browsers such as Chrome can use the browser's password manager to autofill fields in the sign-in process for returning users. For these features to work, the browser needs to be able to distinguish when a user changes their password. Specifically the form for changing the user's password should be cleared or hidden from the page after the new password is set up. If the form for changing the user's password stays filled out on the page after the password change has occurred, the browser may not be able to record the update. </aside> <h3 id="password-managers" data-text="Support password managers" tabindex="-1">Support password managers</h3> <p>Different browsers handle email autofill and password suggestion somewhat differently, but the effects are much the same. On Safari 11 and above on desktop, for example, the password manager is displayed, and then biometric authentication (fingerprint or facial recognition) is used if available.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04.png" alt="Screenshots of three stages of sign-in process in Safari on desktop: password manager, biometric authentication, autofill." width="800" height="234" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-three-stages-b113ab6379e04_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Sign-in with autocomplete—no text entry required!</figcaption> </figure> <p>Chrome on desktop displays email suggestions, shows the password manager, and autofills the password.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3.png" alt="Screenshots of four stages of sign-in process in Chrome on desktop: email completion, email suggestion, password manager, autofill on selection." width="800" height="232" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-four-stages-da9ebdff879c3_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Autocomplete sign-in flow in Chrome 84.</figcaption> </figure> <p>Browser password and autofill systems are not simple. The algorithms for guessing, storing and displaying values are not standardized, and vary from platform to platform. For example, as pointed out by <a href="https://hiddedevries.nl/en/blog/2018-01-13-making-password-managers-play-ball-with-your-login-form">Hidde de Vries</a>: &quot;Firefox&#39;s password manager complements its heuristics with a <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1119454">recipe system</a>.&quot;</p> <p><a href="https://cloudfour.com/thinks/autofill-what-web-devs-should-know-but-dont">Autofill: What web devs should know, but don&#39;t</a> has a lot more information about using <code translate="no" dir="ltr">name</code> and <code translate="no" dir="ltr">autocomplete</code>. The <a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#inappropriate-for-the-control">HTML spec</a> lists all 59 possible values.</p> <aside class="note"><b>Note: </b> You can help password managers by using different <code translate="no" dir="ltr">name</code> and <code translate="no" dir="ltr">id</code> values in sign-up and sign-in forms, for the <code translate="no" dir="ltr">form</code> element itself, as well as any <code translate="no" dir="ltr">input</code>, <code translate="no" dir="ltr">select</code> and <code translate="no" dir="ltr">textarea</code> elements. </aside> <h3 id="password-suggestions" data-text="Enable the browser to suggest a strong password" tabindex="-1">Enable the browser to suggest a strong password</h3> <p>Modern browsers use heuristics to decide when to show the password manager UI and suggest a strong password.</p> <p>Here&#39;s how Safari does it on desktop.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e.png" alt="Screenshot of Firefox password manager on desktop." width="800" height="229" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-firefox-passw-00fc03de0450e_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Password suggestion flow in Safari.</figcaption> </figure> <p>(Strong unique password suggestion has been available in Safari since version 12.0.)</p> <p>Built-in browser password generators mean users and developers don&#39;t need to work out what a &quot;strong password&quot; is. Since browsers can securely store passwords and autofill them as necessary, there&#39;s no need for users to remember or enter passwords. Encouraging users to take advantage of built-in browser password generators also means they&#39;re more likely to use a unique, strong password on your site, and less likely to reuse a password that could be compromised elsewhere.</p> <aside class="note"><b>Note: </b> The downside with this approach is that there's no way to share passwords across platforms. For example, a user may use Safari on their iPhone and Chrome on their Windows laptop. </aside> <h3 id="required-fields" data-text="Help save users from accidentally missing inputs" tabindex="-1">Help save users from accidentally missing inputs</h3> <p>Add the <code translate="no" dir="ltr">required</code> attribute to both email and password fields. Modern browsers automatically prompt and set focus for missing data. No JavaScript required!</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6.png" alt="Screenshot of desktop Firefox and Chrome for Android showing 'Please fill out this field' prompt for missing data." width="600" height="392" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-desktop-firef-eb20cb4ab2ed6_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption"> Prompt and focus for missing data on Firefox for desktop (version 76) and Chrome for Android (version 83). </figcaption> </figure> <h2 id="mobile-design" data-text="Design for fingers and thumbs" tabindex="-1">Design for fingers and thumbs</h2> <p>The default browser size for just about everything relating to input elements and buttons is too small, especially on mobile. This may seem obvious, but it&#39;s a common problem with sign-in forms on many sites.</p> <h3 id="tap-targets" data-text="Make sure inputs and buttons are large enough" tabindex="-1">Make sure inputs and buttons are large enough</h3> <p>The default size and padding for inputs and buttons is too small on desktop and even worse on mobile.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b.png" alt="Screenshot of unstyled form in Chrome for desktop and Chrome for Android." width="800" height="434" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-ddf413b33ec2b_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> </figure> <p>According to <a href="https://support.google.com/accessibility/android/answer/7101858">Android accessibility guidance</a> the recommended target size for touchscreen objects is 7–10 mm. Apple interface guidelines suggest 48x48 px, and the W3C suggest <a href="https://www.w3.org/WAI/WCAG21/Understanding/target-size.html">at least 44x44 CSS pixels</a>. On that basis, add (at least) about 15 px of padding to input elements and buttons for mobile, and around 10 px on desktop. Try this out with a real mobile device and a real finger or thumb. You should comfortably be able to tap each of your inputs and buttons.</p> <p>The <a href="https://developer.chrome.com/docs/lighthouse/seo/http-status-code">Tap targets are not sized appropriately</a> Lighthouse audit can help you automate the process of detecting input elements that are too small.</p> <h4 id="design-for-thumbs" data-text="Design for thumbs" tabindex="-1">Design for thumbs</h4> <p>Search for <a href="https://www.google.com/search?q=touch+target">touch target</a> and you&#39;ll see lots of pictures of forefingers. However, in the real world, many people use their thumbs to interact with phones. Thumbs are bigger than forefingers, and control is less precise. All the more reason for adequately sized touch targets.</p> <h3 id="size-text-correctly" data-text="Make text big enough" tabindex="-1">Make text big enough</h3> <p>As with size and padding, the default browser font size for input elements and buttons is too small, particularly on mobile.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421.png" alt="Screenshot of unstyled form in Chrome on desktop and on Android." width="800" height="494" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-unstyled-form-e9a00e9267421_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Default styling on desktop and mobile: input text is too small to be legible for many users.</figcaption> </figure> <p>Browsers on different platforms size fonts differently, so it&#39;s difficult to specify a particular font size that works well everywhere. A quick survey of popular websites shows sizes of 13–16 pixels on desktop: matching that physical size is a good minimum for text on mobile.</p> <p>This means you need to use a larger pixel size on mobile: <code translate="no" dir="ltr">16px</code> on Chrome for desktop is quite legible, but even with good vision it&#39;s difficult to read <code translate="no" dir="ltr">16px</code> text on Chrome for Android. You can set different font pixel sizes for different viewport sizes using <a href="https://developers.google.com/web/fundamentals/design-and-ux/responsive#apply_media_queries_based_on_viewport_size">media queries</a>. <code translate="no" dir="ltr">20px</code> is about right on mobile—but you should test this out with friends or colleagues who have low vision.</p> <p>The <a href="https://developer.chrome.com/docs/lighthouse/seo/font-size">Document doesn&#39;t use legible font sizes</a> Lighthouse audit can help you automate the process of detecting text that&#39;s too small.</p> <h3 id="size-margins-correctly" data-text="Provide enough space between inputs" tabindex="-1">Provide enough space between inputs</h3> <p>Add enough margin to make inputs work well as touch targets. In other words, aim for about a finger width of margin.</p> <h3 id="visible-inputs" data-text="Make sure your inputs are clearly visible" tabindex="-1">Make sure your inputs are clearly visible</h3> <p>The default border styling for inputs makes them hard to see. They&#39;re almost invisible on some platforms such as Chrome for Android.</p> <p>As well as padding, add a border: on a white background, a good general rule is to use <code translate="no" dir="ltr">#ccc</code> or darker.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe.png" alt="Screenshot of styled form in Chrome on Android." width="250" height="525" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-styled-form-e2baf84fd81fe_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Legible text, visible input borders, adequate padding and margins.</figcaption> </figure> <h3 id="built-in-validation" data-text="Use built-in browser features to warn of invalid input values" tabindex="-1">Use built-in browser features to warn of invalid input values</h3> <p>Browsers have built-in features to do basic form validation for inputs with a <code translate="no" dir="ltr">type</code> attribute. Browsers warn when you submit a form with an invalid value, and set focus on the problematic input.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8.png" alt="Screenshot of a sign-in form in Chrome on desktop showing browser prompt and focus for an invalid email value." width="300" height="290" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshot-a-sign-form-4a4d1d374a2a8_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Basic built-in validation by the browser.</figcaption> </figure> <p>You can use the <code translate="no" dir="ltr">:invalid</code> CSS selector to highlight invalid data. Use <code translate="no" dir="ltr">:not(:placeholder-shown)</code> to avoid selecting inputs with no content.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="CSS"><code translate="no" dir="ltr"><span class="devsite-syntax-nt">input</span><span class="devsite-syntax-o">[</span><span class="devsite-syntax-nt">type</span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-nt">email</span><span class="devsite-syntax-o">]</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-nd">not</span><span class="devsite-syntax-o">(</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-nd">placeholder-shown</span><span class="devsite-syntax-o">)</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-nd">invalid</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">color</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kc">red</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">outline-color</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kc">red</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-p">}</span> </code></pre></devsite-code> <p>Try out different ways of highlighting inputs with invalid values.</p> <h2 id="javascript" data-text="Use JavaScript where necessary" tabindex="-1">Use JavaScript where necessary</h2> <h3 id="password-display" data-text="Toggle password display" tabindex="-1">Toggle password display</h3> <p>You should add a <strong>Show password</strong> toggle to enable users to check the text they&#39;ve entered. <a href="https://www.nngroup.com/articles/stop-password-masking/">Usability suffers</a> when users can&#39;t see the text they&#39;ve entered. Currently there&#39;s no built-in way to do this, though <a href="https://twitter.com/sw12/status/1251191795377156099">there are plans for implementation</a>. You&#39;ll need to use JavaScript instead.</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/sign-in-form.png" alt="Google sign-in form showing Show password toggle and a Forgot password link." width="350" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/sign-in-form_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Google sign-in form: with <strong>Show password</strong> toggle and <strong>Forgot password</strong> link.</figcaption> </figure> <p>The following code uses a text button to add <strong>Show password</strong> functionality.</p> <p>HTML:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Carbon"><code translate="no" dir="ltr">&lt;<span class="devsite-syntax-nx">section</span>&gt; <span class="devsite-syntax-w"> </span>&lt;<span class="devsite-syntax-nx">label</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">for</span><span class="devsite-syntax-p">=</span><span class="devsite-syntax-s">"password"</span>&gt;<span class="devsite-syntax-nx">Password</span>&lt;<span class="devsite-syntax-o">/</span><span class="devsite-syntax-nx">label</span>&gt; <span class="devsite-syntax-w"> </span>&lt;<span class="devsite-syntax-nx">button</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">id</span><span class="devsite-syntax-p">=</span><span class="devsite-syntax-s">"toggle-password"</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">type</span><span class="devsite-syntax-p">=</span><span class="devsite-syntax-s">"button"</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">aria</span><span class="devsite-syntax-o">-</span><span class="devsite-syntax-nx">label</span><span class="devsite-syntax-p">=</span><span class="devsite-syntax-s">"Show password as plain text. Warning: this will display your password on the screen."</span>&gt;<span class="devsite-syntax-nx">Show</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">password</span>&lt;<span class="devsite-syntax-o">/</span><span class="devsite-syntax-nx">button</span>&gt; <span class="devsite-syntax-w"> </span>&lt;<span class="devsite-syntax-nx">input</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">id</span><span class="devsite-syntax-p">=</span><span class="devsite-syntax-s">"password"</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">name</span><span class="devsite-syntax-p">=</span><span class="devsite-syntax-s">"password"</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">type</span><span class="devsite-syntax-p">=</span><span class="devsite-syntax-s">"password"</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">autocomplete</span><span class="devsite-syntax-p">=</span><span class="devsite-syntax-s">"current-password"</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">required</span>&gt; &lt;<span class="devsite-syntax-o">/</span><span class="devsite-syntax-nx">section</span>&gt; </code></pre></devsite-code> <p>Here&#39;s the CSS to make the button look like plain text:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="CSS"><code translate="no" dir="ltr"><span class="devsite-syntax-nt">button</span><span class="devsite-syntax-p">#</span><span class="devsite-syntax-nn">toggle-password</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">background</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kc">none</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">border</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kc">none</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">cursor</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kc">pointer</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c">/* Media query isn't shown here. */</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">font-size</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nf">var</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-nv">--mobile-font-size</span><span class="devsite-syntax-p">);</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">font-weight</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-mi">300</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">padding</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-mi">0</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-c">/* Display at the top right of the container */</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">position</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-kc">absolute</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">top</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-mi">0</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">right</span><span class="devsite-syntax-p">:</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-mi">0</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-p">}</span> </code></pre></devsite-code> <p>And the JavaScript for showing the password:</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="JavaScript"><code translate="no" dir="ltr"><span class="devsite-syntax-kd">const</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">passwordInput</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nb">document</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">getElementById</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s1">'password'</span><span class="devsite-syntax-p">);</span> <span class="devsite-syntax-kd">const</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">togglePasswordButton</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nb">document</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">getElementById</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s1">'toggle-password'</span><span class="devsite-syntax-p">);</span> <span class="devsite-syntax-nx">togglePasswordButton</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">addEventListener</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s1">'click'</span><span class="devsite-syntax-p">,</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">togglePassword</span><span class="devsite-syntax-p">);</span> <span class="devsite-syntax-kd">function</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">togglePassword</span><span class="devsite-syntax-p">()</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">if</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-nx">passwordInput</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">type</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">===</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'password'</span><span class="devsite-syntax-p">)</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">passwordInput</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">type</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'text'</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">togglePasswordButton</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">textContent</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'Hide password'</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">togglePasswordButton</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">setAttribute</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s1">'aria-label'</span><span class="devsite-syntax-p">,</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'Hide password.'</span><span class="devsite-syntax-p">);</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-k">else</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">{</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">passwordInput</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">type</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'password'</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">togglePasswordButton</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">textContent</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">=</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'Show password'</span><span class="devsite-syntax-p">;</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-nx">togglePasswordButton</span><span class="devsite-syntax-p">.</span><span class="devsite-syntax-nx">setAttribute</span><span class="devsite-syntax-p">(</span><span class="devsite-syntax-s1">'aria-label'</span><span class="devsite-syntax-p">,</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'Show password as plain text. '</span><span class="devsite-syntax-w"> </span><span class="devsite-syntax-o">+</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-s1">'Warning: this will display your password on the screen.'</span><span class="devsite-syntax-p">);</span> <span class="devsite-syntax-w"> </span><span class="devsite-syntax-p">}</span> <span class="devsite-syntax-p">}</span> </code></pre></devsite-code> <p>Here&#39;s the end result:</p> <figure> <img src="/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598.png" alt="Screenshots of sign-in form with Show password text 'button', in Safari on Mac and on iPhone 7." width="800" height="468" srcset="https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_36.png 36w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_48.png 48w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_72.png 72w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_96.png 96w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_480.png 480w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_720.png 720w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_856.png 856w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_960.png 960w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_1440.png 1440w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_1920.png 1920w,https://web.dev/static/articles/sign-in-form-best-practices/image/screenshots-sign-form-61274420cf598_2880.png 2880w" sizes="(max-width: 840px) 100vw, 856px"> <figcaption class="wd-caption">Sign-in form with <strong>Show password</strong> text 'button', in Safari on Mac and iPhone 7.</figcaption> </figure> <h3 id="accessible-password-inputs" data-text="Make password inputs accessible" tabindex="-1">Make password inputs accessible</h3> <p>Use <code translate="no" dir="ltr">aria-describedby</code> to outline password rules by giving it the ID of the element that describes the constraints. Screenreaders provide the label text, the input type (password), and then the description.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="HTML"><code translate="no" dir="ltr">&lt;input type="password" aria-describedby="password-constraints" …&gt; &lt;div id="password-constraints"&gt;Eight or more characters with a mix of letters, numbers and symbols.&lt;/div&gt; </code></pre></devsite-code> <p>When you add <strong>Show password</strong> functionality, make sure to include an <code translate="no" dir="ltr">aria-label</code> to warn that the password will be displayed. Otherwise users may inadvertently reveal passwords.</p> <div></div><devsite-code><pre class="devsite-click-to-copy" translate="no" dir="ltr" is-upgraded syntax="Text only"><code translate="no" dir="ltr">&lt;button id="toggle-password" aria-label="Show password as plain text. Warning: this will display your password on the screen."&gt; Show password &lt;/button&gt; </code></pre></devsite-code> <p>You can see both ARIA features in action in the following Glitch:</p> <div class="wd-embed" style="height: 480px;"> <iframe allow="camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi" loading="lazy" src="https://glitch.com/embed/#!/embed/sign-in-form?attributionHidden=true&sidebarCollapsed=true&path=index.html&previewSize=100" style="height: 100%; width: 100%; border: 0;" title="sign-in-form on Glitch" ></iframe> </div> <p><a href="https://webaim.org/techniques/forms/">Creating Accessible Forms</a> has more tips to help make forms accessible.</p> <h3 id="validation" data-text="Validate in realtime and before submission" tabindex="-1">Validate in realtime and before submission</h3> <p>HTML form elements and attributes have built-in features for basic validation, but you should also use JavaScript to do more robust validation while users are entering data and when they attempt to submit the form.</p> <aside class="warning"><b>Warning: </b> Client-side validation helps users enter data and can avoid unnecessary server load, but you must always validate and sanitize data on your backend. </aside> <p><a href="https://glitch.com/edit/#!/sign-in-form-codelab-5">Step 5</a> of the sign-in form codelab uses the <a href="https://html.spec.whatwg.org/multipage/forms.html#constraints">Constraint Validation API</a> (which is <a href="https://caniuse.com/#feat=constraint-validation">widely supported</a>) to add custom validation using built-in browser UI to set focus and display prompts.</p> <p>Find out more: <a href="https://developers.google.com/web/fundamentals/design-and-ux/input/forms#use_javascript_for_more_complex_real-time_validation">Use JavaScript for more complex real-time validation</a>.</p> <h3 id="analytics" data-text="Analytics and RUM" tabindex="-1">Analytics and RUM</h3> <p>&quot;What you cannot measure, you cannot improve&quot; is particularly true for sign-up and sign-in forms. You need to set goals, measure success, improve your site—and repeat.</p> <p><a href="https://www.nngroup.com/articles/discount-usability-20-years/">Discount usability testing</a> can be helpful for trying out changes, but you&#39;ll need real-world data to really understand how your users experience your sign-up and sign-in forms:</p> <ul> <li><strong>Page analytics</strong>: sign-up and sign-in page views, bounce rates, and exits.</li> <li><strong>Interaction analytics</strong>: <a href="https://support.google.com/analytics/answer/6180923">goal funnels</a> (where do users abandon your sign-in or sign-in flow?) and <a href="https://developers.google.com/analytics/devguides/collection/gtagjs/events">events</a> (what actions do users take when interacting with your forms?)</li> <li><strong>Website performance</strong>: <a href="/articles/user-centric-performance-metrics">user-centric metrics</a> (are your sign-up and sign-in forms slow for some reason and, if so, what is the cause?).</li> </ul> <p>You may also want to consider implementing A/B testing in order to try out different approaches to sign-up and sign-in, and staged rollouts to validate the changes on a subset of users before releasing changes to all users.</p> <h2 id="general-guidelines" data-text="General guidelines" tabindex="-1">General guidelines</h2> <p>Well designed UI and UX can reduce sign-in form abandonment:</p> <ul> <li>Don&#39;t make users hunt for sign-in! Put a link to the sign-in form at the top of the page, using well-understood wording such as <strong>Sign In</strong>, <strong>Create Account</strong> or <strong>Register</strong>.</li> <li>Keep it focused! Sign-up forms are not the place to distract people with offers and other site features.</li> <li>Minimize sign-up complexity. Collect other user data (such as addresses or credit card details) only when users see a clear benefit from providing that data.</li> <li>Before users start on your sign-up form, make it clear what the value proposition is. How do they benefit from signing in? Give users concrete incentives to complete sign-up.</li> <li>If possible allow users to identify themselves with a mobile phone number instead of an email address, since some users may not use email.</li> <li>Make it easy for users to reset their password, and make the <strong>Forgot your password?</strong> link obvious.</li> <li>Link to your terms of service and privacy policy documents: make it clear to users from the start how you safeguard their data.</li> <li>Include the logo and name of your company or organization on your signup and sign-in pages, and make sure that language, fonts and styles match the rest of your site. Some forms don&#39;t feel like they belong to the same site as other content, especially if they have a significantly different URL.</li> </ul> <h2 id="resources" data-text="Keep learning" tabindex="-1">Keep learning</h2> <ul> <li><a href="/learn/forms">Create Amazing Forms</a></li> <li><a href="https://www.smashingmagazine.com/2018/08/best-practices-for-mobile-form-design/">Best Practices For Mobile Form Design</a></li> <li><a href="/articles/more-capable-form-controls">More capable form controls</a></li> <li><a href="https://webaim.org/techniques/forms/">Creating Accessible Forms</a></li> <li><a href="https://developer.chrome.com/blog/credential-management-api">Streamlining the Sign-in Flow Using Credential Management API</a></li> <li><a href="/web-otp">Verify phone numbers on the web with the WebOTP API</a></li> </ul> <p>Photo by <a href="https://unsplash.com/photos/_XFObcM_7KU">Meghan Schiereck</a> on <a href="https://unsplash.com">Unsplash</a>.</p> </div> <devsite-thumb-rating position="footer"> </devsite-thumb-rating> <div class="devsite-floating-action-buttons"> </div> </article> <devsite-content-footer class="nocontent"> <p>Except as otherwise noted, the content of this page is licensed under the <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 License</a>, and code samples are licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache 2.0 License</a>. For details, see the <a href="https://developers.google.com/site-policies">Google Developers Site Policies</a>. Java is a registered trademark of Oracle and/or its affiliates.</p> <p>Last updated 2020-06-29 UTC.</p> </devsite-content-footer> <devsite-notification > </devsite-notification> <div class="devsite-content-data"> <template class="devsite-content-data-template"> [[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2020-06-29 UTC."],[],[]] </template> </div> </devsite-content> </main> <devsite-footer-promos class="devsite-footer"> </devsite-footer-promos> <devsite-footer-linkboxes class="devsite-footer"> <nav class="devsite-footer-linkboxes nocontent" aria-label="Footer links"> <ul class="devsite-footer-linkboxes-list"> <li class="devsite-footer-linkbox wd-footer-promo"> <h3 class="devsite-footer-linkbox-heading no-link">web.dev</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <h3 class="devsite-footer-linkbox-heading no-link"> web.dev </h3> <div class="devsite-footer-linkbox-description">We want to help you build beautiful, accessible, fast, and secure websites that work cross-browser, and for all of your users. This site is our home for content to help you on that journey, written by members of the Chrome team, and external experts.</div> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Contribute</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="https://issuetracker.google.com/issues/new?component=1400680&amp;template=1857359" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > File a bug </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://issuetracker.google.com/issues?q=status:open%20componentid:1400680&amp;s=created_time:desc" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > See open issues </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Related Content</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="https://developer.chrome.com/" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > Chrome for Developers </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://blog.chromium.org/" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > Chromium updates </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/case-studies" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Case studies </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/shows" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > Podcasts &amp; shows </a> </li> </ul> </li> <li class="devsite-footer-linkbox "> <h3 class="devsite-footer-linkbox-heading no-link">Follow</h3> <ul class="devsite-footer-linkbox-list"> <li class="devsite-footer-linkbox-item"> <a href="https://twitter.com/ChromiumDev" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 1)" > @ChromiumDev on X </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://www.youtube.com/user/ChromeDevelopers" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 2)" > YouTube </a> </li> <li class="devsite-footer-linkbox-item"> <a href="https://www.linkedin.com/showcase/chrome-for-developers" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 3)" > Chrome for Developers on LinkedIn </a> </li> <li class="devsite-footer-linkbox-item"> <a href="/static/blog/feed.xml" class="devsite-footer-linkbox-link gc-analytics-event" data-category="Site-Wide Custom Events" data-label="Footer Link (index 4)" > RSS </a> </li> </ul> </li> </ul> </nav> </devsite-footer-linkboxes> <devsite-footer-utility class="devsite-footer"> <div class="devsite-footer-utility nocontent"> <nav class="devsite-footer-utility-links" aria-label="Utility links"> <ul class="devsite-footer-utility-list"> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="//policies.google.com/terms" data-category="Site-Wide Custom Events" data-label="Footer Terms link" > Terms </a> </li> <li class="devsite-footer-utility-item "> <a class="devsite-footer-utility-link gc-analytics-event" href="//policies.google.com/privacy" data-category="Site-Wide Custom Events" data-label="Footer Privacy link" > Privacy </a> </li> <li class="devsite-footer-utility-item glue-cookie-notification-bar-control"> <a class="devsite-footer-utility-link gc-analytics-event" href="#" data-category="Site-Wide Custom Events" data-label="Footer Manage cookies link" aria-hidden="true" > Manage cookies </a> </li> </ul> <devsite-language-selector> <ul role="presentation"> <li role="presentation"> <a role="menuitem" lang="en" >English</a> </li> <li role="presentation"> <a role="menuitem" lang="de" >Deutsch</a> </li> <li role="presentation"> <a role="menuitem" lang="es" >Español</a> </li> <li role="presentation"> <a role="menuitem" lang="es_419" >Español – América Latina</a> </li> <li role="presentation"> <a role="menuitem" lang="fr" >Français</a> </li> <li role="presentation"> <a role="menuitem" lang="id" >Indonesia</a> </li> <li role="presentation"> <a role="menuitem" lang="it" >Italiano</a> </li> <li role="presentation"> <a role="menuitem" lang="pl" >Polski</a> </li> <li role="presentation"> <a role="menuitem" lang="pt_br" >Português – Brasil</a> </li> <li role="presentation"> <a role="menuitem" lang="vi" >Tiếng Việt</a> </li> <li role="presentation"> <a role="menuitem" lang="tr" >Türkçe</a> </li> <li role="presentation"> <a role="menuitem" lang="ru" >Русский</a> </li> <li role="presentation"> <a role="menuitem" lang="he" >עברית</a> </li> <li role="presentation"> <a role="menuitem" lang="ar" >العربيّة</a> </li> <li role="presentation"> <a role="menuitem" lang="fa" >فارسی</a> </li> <li role="presentation"> <a role="menuitem" lang="hi" >हिंदी</a> </li> <li role="presentation"> <a role="menuitem" lang="bn" >বাংলা</a> </li> <li role="presentation"> <a role="menuitem" lang="th" >ภาษาไทย</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_cn" >中文 – 简体</a> </li> <li role="presentation"> <a role="menuitem" lang="zh_tw" >中文 – 繁體</a> </li> <li role="presentation"> <a role="menuitem" lang="ja" >日本語</a> </li> <li role="presentation"> <a role="menuitem" lang="ko" >한국어</a> </li> </ul> </devsite-language-selector> </nav> </div> </devsite-footer-utility> <devsite-panel></devsite-panel> </section></section> <devsite-sitemask></devsite-sitemask> <devsite-snackbar></devsite-snackbar> <devsite-tooltip ></devsite-tooltip> <devsite-heading-link></devsite-heading-link> <devsite-analytics> <script type="application/json" analytics>[]</script> <script type="application/json" tag-management>{&#34;at&#34;: &#34;True&#34;, &#34;ga4&#34;: [], &#34;ga4p&#34;: [], &#34;gtm&#34;: [{&#34;id&#34;: &#34;GTM-MZWCJPP&#34;, &#34;purpose&#34;: 0}], &#34;parameters&#34;: {&#34;internalUser&#34;: &#34;False&#34;, &#34;language&#34;: {&#34;machineTranslated&#34;: &#34;False&#34;, &#34;requested&#34;: &#34;en&#34;, &#34;served&#34;: &#34;en&#34;}, &#34;pageType&#34;: &#34;article&#34;, &#34;projectName&#34;: &#34;Articles&#34;, &#34;signedIn&#34;: &#34;False&#34;, &#34;tenant&#34;: &#34;web&#34;, &#34;recommendations&#34;: {&#34;sourcePage&#34;: &#34;&#34;, &#34;sourceType&#34;: 0, &#34;sourceRank&#34;: 0, &#34;sourceIdenticalDescriptions&#34;: 0, &#34;sourceTitleWords&#34;: 0, &#34;sourceDescriptionWords&#34;: 0, &#34;experiment&#34;: &#34;&#34;}, &#34;experiment&#34;: {&#34;ids&#34;: &#34;&#34;}}}</script> </devsite-analytics> <devsite-badger></devsite-badger> <script nonce="kxoO0cwInVU4YTdrHPziBpZturqbfV"> (function(d,e,v,s,i,t,E){d['GoogleDevelopersObject']=i; t=e.createElement(v);t.async=1;t.src=s;E=e.getElementsByTagName(v)[0]; E.parentNode.insertBefore(t,E);})(window, document, 'script', 'https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/js/app_loader.js', '[27,"en",null,"/js/devsite_app_module.js","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web","https://web-dot-devsite-v2-prod-3p.appspot.com",1,null,["/_pwa/web/manifest.json","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/images/video-placeholder.svg","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/favicon.png","https://www.gstatic.com/devrel-devsite/prod/v870e399c64f7c43c99a3043db4b3a74327bb93d0914e84a0c3dba90bbfd67625/web/images/lockup.svg","https://fonts.googleapis.com/css?family=Google+Sans:400,500|Roboto:400,400italic,500,500italic,700,700italic|Roboto+Mono:400,500,700&display=swap"],1,null,[1,6,8,12,14,17,21,25,50,52,63,70,75,76,80,87,91,92,93,97,98,100,101,102,103,104,105,107,108,109,110,112,113,116,117,118,120,122,124,125,126,127,129,130,131,132,133,134,135,136,138,140,141,147,148,149,151,152,156,157,158,159,161,163,164,168,169,170,179,180,182,183,186,191,193,196],"AIzaSyCNm9YxQumEXwGJgTDjxoxXK6m1F-9720Q","AIzaSyCc76DZePGtoyUjqKrLdsMGk_ry7sljLbY","web.dev","AIzaSyB9bqgQ2t11WJsOX8qNsCQ6U-w91mmqF-I","AIzaSyAdYnStPdzjcJJtQ0mvIaeaMKj7_t6J_Fg",null,null,null,["OnSwitch__enable","Cloud__enable_cloud_facet_chat","Analytics__enable_clearcut_logging","MiscFeatureFlags__enable_view_transitions","Profiles__require_profile_eligibility_for_signin","Cloud__enable_cloud_shell_fte_user_flow","Profiles__enable_awarding_url","Profiles__enable_dashboard_curated_recommendations","Profiles__enable_page_saving","Profiles__enable_developer_profiles_callout","Search__enable_dynamic_content_confidential_banner","EngEduTelemetry__enable_engedu_telemetry","Profiles__enable_public_developer_profiles","Cloud__enable_free_trial_server_call","Search__enable_suggestions_from_borg","Cloud__enable_llm_concierge_chat","Search__enable_page_map","MiscFeatureFlags__enable_firebase_utm","TpcFeatures__enable_required_headers","Profiles__enable_recognition_badges","MiscFeatureFlags__enable_project_variables","Cloud__enable_legacy_calculator_redirect","MiscFeatureFlags__enable_variable_operator","Profiles__enable_completecodelab_endpoint","Cloud__enable_cloud_dlp_service","Search__enable_ai_eligibility_checks","Experiments__reqs_query_experiments","CloudShell__cloud_shell_button","BookNav__enable_tenant_cache_key","Profiles__enable_release_notes_notifications","MiscFeatureFlags__developers_footer_dark_image","MiscFeatureFlags__developers_footer_image","Profiles__enable_complete_playlist_endpoint","CloudShell__cloud_code_overflow_menu","Cloud__enable_cloudx_ping","Concierge__enable_pushui","DevPro__enable_cloud_innovators_plus","MiscFeatureFlags__enable_explain_this_code","Cloud__enable_cloudx_experiment_ids","Profiles__enable_profile_collections","DevPro__enable_developer_subscriptions","MiscFeatureFlags__emergency_css","TpcFeatures__enable_mirror_tenant_redirects","Cloud__enable_cloud_shell"],null,null,"AIzaSyA58TaKli1DculwmAmbpzLVGuWc8eCQgQc","https://developerscontentserving-pa.googleapis.com","AIzaSyDWBU60w0P9hEkr29kkksYs8Z7gvZ8u_wc","https://developerscontentsearch-pa.googleapis.com",2,4,null,"https://developerprofiles-pa.googleapis.com",[27,"web","web.dev","web.dev",null,"web-dot-devsite-v2-prod-3p.appspot.com",null,null,[null,null,null,null,null,null,null,null,null,null,null,[1],null,null,null,null,null,null,[1],null,null,null,null,[1,null,1],[1,1,null,1,1]],null,[38,null,null,null,null,null,"/images/lockup.svg","/images/touchicon-180.png",null,null,null,1,1,null,null,null,null,null,null,null,null,2,null,null,null,"/images/lockup-dark-theme.svg",[]],[],null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[[],[1,1]],[[null,null,null,null,null,["GTM-MZWCJPP"],null,null,null,null,null,[["GTM-MZWCJPP",1]],1]],null,4]]') </script> <devsite-a11y-announce></devsite-a11y-announce> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10