Do & Learn
Published — September 16th, 2024
Using enterkeyhint Attribute to Build Better Mobile Forms
If you're looking to improve how you build your mobile forms, you've come to the right place! In our previous post, "Inputmode Attribute Explained: Key to Better Mobile Forms", we explored how the inputmode attribute can improve input fields for mobile devices. Today, we're continuing that story with another valuable attribute: the enterkeyhint. This small yet powerful attribute can greatly enhance the user experience and accessibility of your forms on mobile devices. Let’s explore how to use this attribute and learn how to create more intuitive and user-friendly forms.What is enterkeyhint?The enterkeyhint is a global HTML attribute that allows developers to tell the users what action will occur when they press the enter key on a virtual keyboard, such as submitting a form, moving to the next input field or inserting a new line. While inputmode controls what kind of virtual keyboard will be shown, enterkeyhint indicates how the enter key will be labeled (or which icon will be shown).Providing a clear and relevant label or icon can make your form interactions smoother and more intuitive, especially on mobile devices where keyboard space is limited.How Does enterkeyhint Work?The enterkeyhint works on any form fields such as input, textarea or any other element that is editable (has contenteditable=true). It is an enumerated attribute and only accepts the following values:enterkeyhint="enter"Indicates that pressing the enter key on a virtual keyboard will insert a new line. The label shown depends on user agent and user language but it is typically something like: ↵ or 'Return'.For example, if you have a multi-line text input (like a textarea) and you set enterkeyhint="enter", the virtual keyboard will display the enter key with a label indicating that it performs a line break or simply inserts a new line, rather than performing a specific action such as form submission or search. enterkeyhint="done"Indicates that pressing the enter key on a virtual keyboard will complete the current action, usually submitting a form.This hint is typically used in forms where the user is expected to finish entering data and submit it. For example, if you have a text input field in a form, setting enterkeyhint="done" will display a "Done" label on the enter key of the virtual keyboard, signaling that pressing it will submit the form or complete the current input.enterkeyhint="go"Indicates to the user that pressing the enter key on a virtual keyboard will trigger a "go" action. This is typically used in contexts where the user is expected to submit a search query or initiate a navigation action.For example, if you have a search input field, setting enterkeyhint="go" would display a "Go" label on the enter key of the virtual keyboard, signaling to the user that pressing it will start the search process.enterkeyhint="next"Indicates that pressing the enter key on a virtual keyboard will move the focus to the next input field or element in the form rather than submitting the form or performing another action. It would display a "Next" label on the enter key.This makes the navigation between fields more intuitive for users.enterkeyhint="previous" Indicates that pressing the enter key on a virtual keyboard will move the focus to the previous input field or element in the form. It would display a "Previous" label on the enter key. This is probably not that commonly used like the "next" hint, however is can be useful in multi-field forms where users may need to navigate backward through fields they have already filled out. enterkeyhint="search" Indicates that pressing the enter key on a virtual keyboard will trigger a search action. This is useful for input fields where users are expected to enter search queries. By setting enterkeyhint="search", the virtual keyboard will display a label such as "Search" on the enter key.enterkeyhint="send" Indicates that pressing the enter key on a virtual keyboard will trigger a send action. It would display a "Send" label on the enter key. This is useful in contexts where the user is expected to send a message or submit a communication.If no enterkeyhint attribute is provided, the label on the enter key on the virtual keyboard defaults to the browser's or device's standard settings which may be generic, such as "Enter" or "Return" or if inputmode, type, or pattern attributes are used, the user agent might use contextual information from these attributes and display a suitable label or icon.Note: Applying the enterkeyhint attribute only informs the user what will happen on press on the Enter key. To fully improve the user experience and the accessibility of your forms you must also implement the sutable functionality such as automatically navigating though the form fields on keypress or submitting the form on press on Enter.ExamplesLet's see it in action. Bellow is a multi-step form with three simple fields and a button that is responsible for submitting the form. <form> <input type="text" enterkeyhint="next" placeholder="First name" /> <input type="text" enterkeyhint="next" placeholder="Last name" /> <input type="text" enterkeyhint="done" placeholder="Address" /> <button type="submit">Save</button> </form> As users fill out each field and press enter, they are guided through the form fields in order due to the enterkeyhint="next" attribute. When they reach the last field, the enterkeyhint="done" attribute signals that pressing on Enter key at this point will submit the form. Try out the example on your mobile device or using a virtual keyboard. Click on any field and notice the label in the right bottom corner. Save It should look something like this. (This example is on iOS, the keyboard shown will vary depending on what type of device is used). You can also apply this attribute dynamically using the enterKeyHint property of on a HTMLElement const firstName = document.getElementById("firstName"); const lastField = document.getElementById("lastName"); firstName.enterKeyHint = "Next"; lastField.enterKeyHint = "Done";When to Use enterkeyhintThe enterkeyhint attribute is useful when you want to control the label or icon displayed on the "enter" key on virtual keyboards. You should use it in cases where the default behaviour doesn't fully match the user experience you're trying to provide. Here are situations where you can use its benefits: Forms with Multiple Steps - As shown in the example above, when there are multiple fields or steps in a form and the functionality for field navigation is implemented, using enterkeyhint="next" or enterkeyhint="previous" can improve the user experience by informing them what will happen when they press Enter key. Search Fields - Probably the most common scenario is to apply enterkeyhint="search" on a search input field that performs a search on press on Enter key. Sending a Message- Similar to search fields, the attribute can be applied to an input or textarea fields that contain a message that will be sent on press on Enter key. Custom Actions - Sometimes, even though the browser can infer an appropriate label, you might want to override it for a better user experience. For example, on a numeric input field, the default key might be "Next," but you want the key to indicate "Go" or "Done."Browser supportenterkeyhint attribute is supported by most major browsers as shown on the table below. :root { --post-background-color: #2e5ba6; --post-content-color: #ffffff;} form.enterkeyhint-form-example { display: flex; flex-direction: column; gap: 1em; }
Latest posts
Privacy First: How to Securely Handle User Data in Forms
— Do & Learn
When working with forms that handle sensitive information like personal details, passwords, or financial data, paying attention to privacy is crucial. It’s about respecting your users and protecting their personal information. Every time you collect or process data through a form, you need to be clear about how you will use, store, or share this information. Here are some simple steps to help you build trust with your users.Limit Data Collection Only ask for the information you need to make your form work. For example, if you’re creating a signup form for a newsletter, you only need the user’s email address. Avoid requesting extra details such as phone numbers or addresses unless they are necessary. Similarly, for a contact form, asking for just a name and email address may be enough in most situations.Collecting only necessary data does not only reduces the risk of exposing personal information but also improves the user experience. Short, straightforward forms are easier to complete and less frustrating for users.Tip: Regularly check your forms to ensure they only collect necessary information. This keeps your forms user-friendly and helps protect personal data.Provide Clear Data Collection DetailsClearly inform users about what data you’re collecting and why. Make sure to always ask for their explicit permission before processing their sensitive information. Use clear labels: For example, instead of just a checkbox that says “I agree,” use labels like “I agree to receive emails at this address” Provide additional text: Include a brief description near the checkbox or consent request. For example, “We will use your email address only to send you updates and offers. You can unsubscribe at any time.” Highlight important details: If you are collecting sensitive information, make sure to explain why it’s needed and how it will be protected. For example, “We need your phone number to provide customer support. Your number will not be shared with third parties.”Tip: Make sure the consent request is easy to understand and does not use complicated legal terms. This helps users make informed decisions about their data.Bad approach: I agree Sign UpGood approach: I agree to receive newsletters at this email address. (We will use your email only to send updates and offers. You can unsubscribe at any time.) Sign UpHide/Mask Sensitive InformationFor fields that require sensitive information such as passwords, credit card numbers and CCV, use masking techniques to hide the user's input. This ensures that user's sensitive data is not exposed while they are typing. For example, show asterisks (****) instead of the actual characters in password fields, CVV or any other similar information.Bad approach: Password: CVV: SubmitGood approach: Password: CVV: SubmitAllow Data Access and UpdateGive users the option to view their data, make updates, or delete it if they want to. For example, if a user wants to change their email address, they should be able to update it easily in their account settings. This will help users feel in control of their personal information and build trust.Update Privacy Practices RegularlyStay up-to-date with changes in privacy laws, such as GDPR (General Data Protection Regulation) and CCPA (California Consumer Privacy Act). Regularly review and update your privacy practices to ensure compliance with these laws.Tip: Consider having a privacy expert review your policies periodically to ensure they meet legal requirements and best practices.ConclusionHandling user data with care is a legal requirement but also essential for building trust with your users. By following these steps, you can protect user privacy and make sure your forms are secure and trustworthy. Showing that you are committed to protecting personal information helps create a safe environment for your users, therefore increases your user acitvity and engagement. :root { --post-background-color: #005ae8; --post-content-color: #ffffff;}
Inputmode Explained: The Key to User-friendly Mobile Forms
— Do & Learn
If you’ve ever struggled with typing the right information into a web form on your phone, you’ll appreciate how much a simple change like inputmode can improve the experience. But what exactly is inputmode, and how can you use it to make your forms more user-friendly?What is Inputmode?inputmode is an HTML attribute that tells the browser which type of virtual keyboard to display when a user focuses on an input field. By showing the appropriate keyboard, it makes typing faster and reduces errors. It’s especially useful on mobile devices where keyboard layouts can change based on the type of data you need to enter. inputmode is primarily used on <input /> elements but it can also be applied on any other HTML element in contenteditable mode. How Does inputmode Work?When you add the inputmode attribute to an input field, you specify the type of data you expect users to enter. The browser then displays the most relevant keyboard. Here are all the values that inputmode can have: text: Default keyboard for text input. numeric: Number pad for numerical input. decimal: Number pad with a decimal point. tel: Telephone keypad for entering phone numbers. It includes the digits 0–9, the asterisk (*), and the pound (#) key email: Keyboard optimised for email addresses. Includes @ and .com buttons. url: Keyboard optimised for URLs (includes / and . buttons). search: Keyboard optimised for search input. For example, the return/submit key may be labeled "Search", along with possible other optimisations. none: No virtual keyboard. For when the page implements its own keyboard input control.ExamplesLet’s look at some examples of how to use inputmode in your forms:Numeric <label for="amount">Amount:</label> <input type="text" id="amount" inputmode="numeric" placeholder="Enter amount"/>The keypad on mobile will look something like this. (This can vary depending on the mobile OS, this example shows how it looks on iOS)Tel <label for="phone">Phone:</label> <input type="text" id="phone" inputmode="tel" placeholder="Enter your phone number"/>The keypad on mobile will look something like this:Email <label for="email">Email:</label> <input type="text" id="email" inputmode="email" placeholder="Enter your email"/>The keypad on mobile will look something like this:UrlHere is an example of using a <div> element in editable mode as a field: <label for="url">Url:</label> <div contenteditable="true" id="url" inputmode="url" placeholder="Enter a url"/>The keypad on mobile will look something like this:When to Use inputmodeIf you're using the HTML <input> element with a specific type attribute (other then 'text'), the keypad will be automatically shown depending on the type. However, if you need a more custom input or other editable element then you might need to use the proper inputmode. Using the correct inputmode will allow you to optimise the input experience for users, especially on mobile devices. Here are some situations where it can be helpful: Numeric: Credit Card Numbers Security Codes (e.g., CVV for credit cards) Bank Account Numbers Social Security Numbers Employee or Student ID Numbers Membership Numbers Serial Numbers for Products Decimal: Prices Weights Product Dimensions Coordinates (e.g., latitude and longitude) Financial Data (e.g., interest rates) Measurement Units (e.g., height, length) Tel: Phone Numbers Fax Numbers Email: Email Addresses Contact Forms URL: Website URLs API Endpoints Search: Search Fields on Websites Search Boxes in Applications Text: Names Addresses General Text Inputs By making these small adjustments, you can significantly improve the user experience on your web forms, making them quicker and easier to fill out.Browser supportinputmode attribute is supported by most major browsers as shown on the table below. :root { --post-background-color: #bee4da; }
Creating Accessible Forms in React
— Do & Learn
Why is accessibility in web forms important Accessibility in web forms ensures that all users, including those using assistive technologies such as screen readers and keyboard-only navigation, can interact with and complete web forms easily. There are already established accessibility standards and best practices that developers can follow to create inclusive online web forms that improve the overall user experience by making it usable for everyone. (Some resources are included at the end of this post).Semantic HTML form elements One of the most important step to creating accessible forms is using the proper semantic HTML form elements such as: <form>, <input>, <label>, <select>, <textarea>, <button>, <fieldset>, <legend>, <datalist>, <output>, <option>, <optgroup>. All of these elements clearly describe their meaning in a human and machine-readable way and provide context to web content, enabling assistive technologies to interpret and interact with the content accurately. Properly structured semantic HTML elements also enable better keyboard navigation essential for users who rely on keyboards or other input devices other than mouse. Using the correct element such as <form> will ensure that pressing the Tab key through a form follows a logical order, that will make form completion easier, or using a <button> element will trigger form submission by default when the Enter key is pressed. In the same way, associating a <label> with an <input> helps screen readers announce the label when the input field is focused, which helps users understand the purpose of the field. In some situations, custom form elements might be needed if the provided HTML elements are not enough for the purpose of if a more customised look is required. In which case it is very important to make the custom elements accessible using the proper ARIA (Accessible Rich Internet Applications) roles and attributes. However, relying on semantic HTML elements whenever possible will certainly reduce the need for additional ARIA roles and properties which will minimise complexity and potential errors.How to create accessible formsPlaceholders Most often, placeholder text is used to provide instructions or an example of what kind of data is required for a certain form field. It is usually displayed with lower color contrast and it disappears when the user starts typing. Placeholders can provide valuable guidance for many users, however it is important to always use it alongside a label as assistive technologies do not treat placeholders as labels.LabelsLabel elements provide clear, descriptive text associated with form fields that allow all users to better understand the purpose of each field. Here are a few approaches of using label elements when creating accessible forms.Explicitly associating labels with form fields: The most common and recommended approach is using the for attribute on a <label> element to associate it with an <input> element by matching the for attribute with the id of the input element. <label for="username">Username</label> <input type="text" id="username" name="username"/>Wrapping the form field in label element: Sometimes the id of a form field might not be known or even present. In cases like this, the <label> is used as a container for both the label text and the form field so that the two are associated implicitly. <label> Username <input type="text" name="username"/> </label>However, explicitly associating labels is generally better supported by assistive technology.Using additional instructions aside from labels In some cases, additional instructions aside from the label might be required. Such as: a helper text below the input field or an additional description bellow a label element. To make the form accessible in scenarios like this, you can use the aria-labelledby and aria-describedby attributes. Here is an example: <label id="dateLabel" for="dateOfBirth"> Date of birth:</label> <input type="text" name="dateOfBirth" id="dateOfBirth" aria-labelledby="dateLabel dateHelperText"> <span id="dateHelperText">MM/YYYY</span> <label id="dateLabel" for="dateOfBirth">Date of birth:</label> <input type="text" name="dateOfBirth" id="dateOfBirth" aria-labelledby="dateLabel" aria-describedby="dateHelperText"> <span id="dateHelperText">MM/YYYY</span> Hiding the labelSometimes, the design requires omitting a label from the form field, but it is still necessary for a fully accessible form. Fortunately, there is a solution you can apply in situations like this.Hiding the label visuallyThe most common approach used is to hide the label visually but keep it available to the assistive technology devices. It can be achieved by using css to hide the element. <label for="example" class="visually-hidden">Example Label</label> <input type="text" id="example" name="example"/> .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; }Please note that using visibility: hidden will not work properly in this case. The label must be hidden by displaying it in a 1 pixel area like in the example in order for the screen readers to interpret it.Labelling buttonsWhen it comes to labeling buttons, there are also a few possible solutions to make it accessible. Let's take a look at some.Adding the label inside the elementThe standard and most common approach is adding a visible text inside the button element. <button>Submit</button>Using aria-label AttributeThis attribute provides an accessible name to button elements that don't have visible text, usually buttons that contain an icon. <button aria-label="Submit">✔️</button>Using title AttributeAlternatively, the text can be placed in the title attribute. This attribute can provide additional information, although it is less preferred than aria-label.Visually hidden labelA more accessible alternative for buttons that don't have visible text is an approach similar to the visually hidden label: text that is visually hidden using CSS next to the icon. <button> <span class="visually-hidden">Submit</span> ✔️ </button>Using the value attributeWhen using an <input type="button"> for a button element, the label can be placed in the value attribute. <input type="button" value="Submit" />Using image as buttonIf the image button <input type="image"> is used, the label is set in the alt attribute. <input type="image" src="button.png" alt="Submit">Grouping elementsGrouping form elements using the appropriate HTML elements such as <fieldset> and <legend> provides semantic meaning to assistive technologies. Screen readers can understand the relationship between form elements within the group, making it easier for the users to understand the form as well.Grouping form elements also allows users to navigate between related fields more easily using the keyboard. Users can typically jump between form fields within the group using the Tab key, improving the overall usability and accessibility of the form.When a group of form elements is focused, assistive technologies can announce the group's label or legend, providing users with context about the purpose of the group. This helps users better understand the structure of the form.Grouping related fields using Fieldset and LegendThe <fieldset> element semantically groups related form fields which allows assistive technologies to interpret and understand their relationship. Visually grouped elements also make the form easier to understand and use by any user.The <fieldset> element is used in combination with the <legend> element, which provides a caption for the group. Here is an example: <form> <fieldset> <legend>Personal Information</legend> <label for="firstName">First Name:</label> <input type="text" id="firstName" name="firstName"> <label for="lastName">Last Name:</label> <input type="text" id="lastName" name="lastName"> </fieldset> <fieldset> <legend>Address</legend> <label for="street">Street:</label> <input type="text" id="street" name="street"> <label for="city">City:</label> <input type="text" id="city" name="city"> </fieldset> <button type="submit">Submit</button> </form>Radio Buttons, Checkboxes or related fields must also be grouped using <fieldset> with corresponding <legend>.For select elements with groups of options, the <optgroup> element can be used to indicate such groups. The label attribute of the <optgroup> element is used to provide a label for the group.Grouping related fields with WAI-ARIAWhen using <fieldset> and <legend> is not an option (perhaps the design requires more custom element), the same grouping of elements can be achieved by associating the related fields using WAI-ARIA attributes. Such attributes are: aria-labelledby and aria-describedby. For example, aria-labelledby can link a group of related fields to a heading that describes their collective purpose ensuring that screen readers correctly interpret this relationship to the users. Additionally, applying the attribute role="group" on an element such as <div> can be used to define a logical grouping of related fields, providing a semantic structure that assistive technologies can read.Let's modify the code of the previous example by using WAI-ARIA attributes to associate related fields without using <fieldset> and <legend>: <form> <div role="group" aria-labelledby="personalInfoHeading"> <h2 id="personalInfoHeading">Personal Information</h2> <label for="firstName">First Name:</label> <input type="text" id="firstName" name="firstName"> <label for="lastName">Last Name:</label> <input type="text" id="lastName" name="lastName"> </div> <div role="group" aria-labelledby="addressHeading"> <h2 id="addressHeading">Address</h2> <label for="street">Street:</label> <input type="text" id="street" name="street"> <label for="city">City:</label> <input type="text" id="city" name="city"> </div> <button type="submit">Submit</button> </form>Keyboard NavigationAs mentioned in this guide, enabling proper keyboard navigation is a very important step when creating accessible forms to provide a good user experience to users that use keyboard for navigating though the web. If you rely mostly on using semantic HTML elements, this is not something you need to worry about. However, when a certain custom element needs to be incorporated in a web form, you might need to follow some of the establish standards on how it should function so that you can provide proper keyboard navigation and full accessibility in your form.Depending on the type and purpose of element, the standards can slightly differ. For example, creating a custom <select> (comobox) element might require different keyboard navigation than a <button> element would. Since this is a bit larger topic, in this guide we'll only mention a few of the most common requirements for a custom form field.For example, each element contained in a form should be focused and interacted with using the Tab key. This is usually achieved by using the tabindex attribute. Some elements such as <select>, <input> or <button> should have a keyDown event listener on Enter key that will allow the specific option to be selected, the specific button clicked or the form to be submitted. In the same way, pressing the Escape key should dismiss open popups/menus and similar elements.All standards, rules and patterns for specific elements can be found on the WAI-ARIA documentation.Form validation and ErrorsEffective form validation includes providing clear information about required and optional fields along with concise error messages that are accessible to screen readers and other assistive technologies.Required fieldsRequired fields in forms are usually marked by using either the required attribute, the * symbol next to the label or both. While this may provide a good visual experience for some users, to make sure that all users can easily interact with the form additional attributes such as aria-required should be used, especially when using custom elements other than the semantic HTML form elements.Most current web browsers automatically set the value of aria-required to true when the HTML5 required attribute is present.Please note that the aria-required attribute, like all ARIA states and properties, only helps the screen readers and such devices to interpret an element as required but they have no impact on element functionality. Functionality and behaviour must be added in with JavaScript.Displaying errorsDisplaying errors in forms can be as simple as showing a certain text containing a message explaining the error for incorrect input. When using the semantic HTML elements like input with the specific type attribute such as: 'date', 'number', 'tel', 'email' etc. such message is shown by default that is already adapted for accessibility. But when a more customised error message is needed, specific attributes must also be applied to ensure accessibility.The attributes aria-invalid and aria-errormessage should be used together to indicate an error in a form field. Both aria-invalid and aria-errormessage are applied to the input field while the aria-invalid is a boolean value indicating if there is an error or not, and aria-errormessage contains the id of the element where the error message is shown. The aria-errormessage attribute should only be used when the value of a field is not valid that is when aria-invalid is set to 'true'. If the field is valid and you include the aria-errormessage attribute, make sure the element referenced is hidden, as the message it contains is not relevant.Here is an example: <label for="email">*Email address:</label> <input id="email" type="email" name="email" aria-invalid="true" aria-errormessage="emailError" /> <span id="emailError">Incorrect email</span>However, the screen reader won't automatically read the error message when it appears solely based on the presence of aria-errormessage attribute. To allow announcing the error message when there is an error on change on the input or on form submit, you should apply another attribute on the element that contains the error message: aria-live. This attribute can have one of the three values: assertive - Indicates that updates to the region have the highest priority and should be presented to the user immediately. off (default) - Indicates that updates to the region should not be presented to the user unless the user is currently focused on that region. polite - Indicates that updates to the region should be presented at the next graceful opportunity, such as at the end of speaking the current sentence or when the user pauses typing. FormFusion and accessibilityIf you are like me and you are not a fan of repeting code or you simply don't want to worry too much about accessibility you can always use libraries such as FormFusion that make creating forms easy and worry-free. The library offers fully accessible form elements easy to customise along with built-in validation.Here is an example of the form mentioned previously, built using FormFusion: import { Form, Input } from 'formfusion'; import './App.css'; function App() { return ( <Form> <fieldset> <legend>Personal Information</legend> <Input type="alphabetic" id="firstName" name="firstName" label="First name" classes={{ root: 'formControl', error: 'formControl__error' }} /> <Input type="alphabetic" id="lastName" name="lastName" label="Last name" classes={{ root: 'formControl', error: 'formControl__error' }} /> <Input type="email" id="email" name="email" label="Email" required classes={{ root: 'formControl', error: 'formControl__error' }} /> </fieldset> <fieldset> <legend>Address</legend> <Input type="text" id="street" name="street" label="Street" classes={{ root: 'formControl', error: 'formControl__error' }} /> <Input type="alphabetic" id="city" name="city" label="city" classes={{ root: 'formControl', error: 'formControl__error' }} /> </fieldset> <button type="submit">Save</button> </Form> ); } export default App;FormFusion will automatically add all of the necessary attributes to make the form fully accessible. It will also handle the validation and how the errors are displayed depending on the selected validation method (validateOnChange or validateOnBlur). The final HTML structure of this code in the browser, assuming that the email field was already interacted with, will look like this: <form class="FormFusion"> <fieldset> <legend>Personal Information</legend> <div class="FormFusion-Input__root formControl"> <label for="firstName" class="FormFusion-Input__root__label">First name</label> <input id="firstName" name="firstName" class="FormFusion-Input__root__field" type="text" pattern="^[a-zA-Z\s]+" data-type="alphabetic" value="" aria-invalid="false" /> <span class="FormFusion-Input__root__error formControl__error" id="FormFusion-firstName-error" aria-live="polite"> </span> </div> <div class="FormFusion-Input__root formControl"> <label for="lastName" class="FormFusion-Input__root__label">Last name</label> <input id="lastName" name="lastName" class="FormFusion-Input__root__field" type="text" pattern="^[a-zA-Z\s]+" data-type="alphabetic" value="" aria-invalid="false" /> <span class="FormFusion-Input__root__error formControl__error" id="FormFusion-lastName-error" aria-live="polite"> </span> </div> <div class="FormFusion-Input__root formControl"> <label for="email" class="FormFusion-Input__root__label">Email</label> <input id="email" name="email" required="" class="FormFusion-Input__root__field" type="email" data-type="email" value="" aria-invalid="true" aria-errormessage="FormFusion-email-error" /> <span class="FormFusion-Input__root__error formControl__error" id="FormFusion-email-error" aria-live="polite"> Please include an '@' in the email address. 'sa' is missing an '@'. </span> </div> </fieldset> <fieldset> <legend>Address</legend> <div class="FormFusion-Input__root formControl"> <label for="street" class="FormFusion-Input__root__label">Street</label> <input id="street" name="street" class="FormFusion-Input__root__field" type="text" value="" aria-invalid="false" /> <span class="FormFusion-Input__root__error formControl__error" id="FormFusion-street-error" aria-live="polite"> </span> </div> <div class="FormFusion-Input__root formControl"> <label for="city" class="FormFusion-Input__root__label">City</label> <input id="city" name="city" class="FormFusion-Input__root__field" type="text" pattern="^[a-zA-Z\s]+" data-type="alphabetic" value="" /> <span class="FormFusion-Input__root__error formControl__error" id="FormFusion-city-error" aria-live="polite"> </span> </div> </fieldset> <button type="submit">Save</button> </form>The full code for this example can be found on Github and Stackblitz.ConclusionAccessibility in web forms is crucial for creating inclusive web applications. By using semantic HTML elements, following best practices, and applying ARIA roles and attributes, developers can ensure all users, including those that use assistive technologies, can easily complete forms.Well-structured forms with clear labels, logical tab order, and accessible error messages improve usability for everyone. Tools like FormFusion simplify this process by providing accessible form components.Prioritising accessibility from the start ensures a better user experience for all and contributes to a more inclusive digital world.Resources https://developer.mozilla.org/en-US/docs/Web/Accessibility https://www.w3.org/WAI/ARIA/apg/patterns/ https://www.w3.org/WAI/standards-guidelines/aria/ :root { --post-background-color: #511c69; --post-content-color: #ffffff;}
Different Ways to Handle Form Submission in React
— Do & Learn
Much like HTML, in React, forms are also used to allow user interaction within web pages. Adding a form in React is as simple as adding any other element: <form> {/* Form fields go here */} </form> However, the default behaviour of form submission in React typically isn't what we want. Instead, we aim to override this default behaviour and allow React to manage the form. This gives us more control over how the form data changes and how it is submitted. There are two common approaches used in React when building forms: Controlled and uncontrolled forms.Controlled forms In a controlled form, the values of the form fields are controlled by the React component, usually stored in the component's state. This means that every change of a value in the field triggers a state update, which then re-renders the component with the updated value.Controlling form data using the useState hook The first approach that usually comes to mind when building controlled forms is using the component's state to store the values of the fields. Specifically, the useState hook (in functional components). Here is a simple example of how this looks within a small form with one input field: import { useState } from "react"; function ControlledForm() { const [name, setName] = useState(""); return ( <form> <label>Enter your name: <input type="text" value={name} onChange={(e) => setName(e.target.value)} /> </label> </form> ) } export default ControlledForm; This approach might seem fine for simple forms, but as a form grows, the code can get more and more complex. Imagine if there are 15 fields in a form. There will have to be a useState hook for each of the 15 form fields. This will make maintaining the state and code harder. In situations that require large and complex forms, using a different approach than the component's state is a better idea. Such an approach is using React's useReducer hook.Controlling form data using the useReducer hook In React, a reducer is a function that manages complex state logic in a more organized and predictable way. It takes the current state and an action and calculates the new state based on that action. It's like having a set of instructions for how to update your state depending on what action is performed. The useReducer is a React hook that allows you to use reducers in functional components. Let's improve the previous example to use useReducer instead of useState. First, we're going to define the initial state. Since we only have one field for now, we'll add a new state property values that will be an object with one property: name, which is the name of our form field.In a new file that will hold the reducer function, we'll define and export our initial state: export const initialState = { values: { name: '' } };Next, we will define the reducer function. Since this is the simplest example, we'll add only one action that will describe what we want to change in the state. Let's name the action SET_VALUE. The task of this action will be to update the specific value of the 'name' field. export function formReducer(state, action) { switch (action.type) { case 'SET_VALUE': return { ...state, values: { ...state.values, [action.payload.field]: action.payload.value, }, }; default: return state; } }If you're not familiar with how a reducer works, here is a brief explanation of the code:The formReducer function takes two arguments: state (the current state of the form) action (an object describing what changes to make to the state) switch (action.type): This checks what type of action is being performed.case 'SET_VALUE': If the action type is 'SET_VALUE', the reducer will update the values object in the state.return {...state, ...}: This line creates a new state object by copying the existing state.values: {...state.values, ...}: This line creates a new values object by copying the current values from the state.[action.payload.field]: action.payload.value: This updates a specific field in the values object. The field to update and the new value come from action.payload (the action payload is passed when the action is triggered from the form component. You'll see this later in this post).default: If the action type is not 'SET_VALUE', the reducer just returns the current state without making any changes.Next, we'll need to update the form component to use our new reducer to manage the form data instead of the useState hook. import { useReducer } from 'react'; import { formReducer, initialState } from './reducer'; function ControlledForm() { const [state, dispatch] = useReducer(formReducer, initialState); return ( <form> <label>Enter your name: <input type="text" value={state.values.name} onChange={(e) => dispatch({ type: 'SET_VALUE', payload: { field: 'name', value: e.target.value }, }) } /> </label> </form> ) } export default ControlledForm;Now, let's break down the code. First, we import the reducer and initial state we defined in the previous step and pass them into the useReducer hook. The useReducer hook returns an array with two elements: state: will always hold the current state of the component dispatch: a function that is called to update the state. When dispatch is called with an action object, the formReducer function will process this action and return the new stateAnd finally, in the onChange method, we call the dispatch function with the action object. The action object contains two properties: type: the type of the action we want to trigger. In this case, it's the SET_VALUE action that we added in our reducer. payload: an object containing the data we want to update in our state. In this case, we're passing the name of the field we want to update and the value of the field that we want to update it to.That's it! We've just updated our form to use useReducer, and now we can add a lot more form fields while keeping the code clean and easily maintainable.We can also add new actions in our reducer, for example: an action that will keep info about which fields are touched, or an action that will handle errors in our form, etc.Uncontrolled formsUncontrolled forms rely on the browser to manage the form data. In this approach, form fields maintain their own state, and React components only need to access the DOM elements to retrieve the data when needed.Uncontrolled form using the useRef hookWhen building uncontrolled forms, the useRef hook is often used to access the value of each form field. useRef is a hook that returns a mutable ref object. This object has a current property that can be used to store a value or a reference to a DOM element. Unlike state, updating a ref does not cause the component to re-render. import { useRef } from 'react'; function UncontrolledForm() { const nameRef = useRef(); return ( <form onSubmit={(e) => { e.preventDefault(); alert(nameRef?.current.value); }}> <label>Enter your name: <input ref={nameRef} type="text" /> </label> </form> ); } export default UncontrolledForm;This will work great for simpler forms. However, just like when using useState, this approach also becomes hard to maintain once the form grows and a lot more fields are needed. That's why I'd always recommend using the magical FormData object.Uncontrolled form using the FormData objectThe FormData object is a built-in JavaScript object that provides a way to easily construct a set of key/value pairs representing form fields and their values. It is commonly used to send form data. It is also important to note that FormData will only use input fields that use the name attribute.Let's see this in code. First, we'll take the previous example and replace the input ref with a new ref for our form. We'll need this in order to use the FormData object on our form element. import { useRef } from 'react'; function UncontrolledForm() { const formRef = useRef(); ... } export default UncontrolledForm;Next, we'll define a new function that will handle the form submission. import { useRef } from 'react'; function UncontrolledForm() { const formRef = useRef(); const handleSubmit = (e) => { // prevent the default submit behavior that will refresh the page e.preventDefault(); // construct the key/value pairs from the form element. const formData = new FormData(formRef.current); const formValues = {}; // map each key/value pair to a new object formData.forEach((value, key) => { formValues[key] = value; }); alert(JSON.stringify(formValues, null, 2)); }; ... } export default UncontrolledForm;When you create a new FormData object and pass a form element to it, it automatically collects all the data (field values) from that form.The complete code looks like this: import { useRef } from 'react'; function UncontrolledForm() { const formRef = useRef(); const handleSubmit = (e) => { e.preventDefault(); const formData = new FormData(formRef.current); const formValues = {}; formData.forEach((value, key) => { formValues[key] = value; }); alert(JSON.stringify(formValues, null, 2)); }; return ( <form ref={formRef} onSubmit={handleSubmit}> <label>Enter your name: <input name="name" type="text" /> </label> <button type="submit">Submit</button> </form> ); } export default UncontrolledForm;That's it. You can now add any fields you like, and they will be automatically picked up by the FormData object as long as they have the name attribute.Uncontrolled form using the new React form action attributeAnother way to build uncontrolled forms is using the new React extension to the form element, specifically the improved action attribute. Unlike the standard action attribute on the form element, the new improvements allow us to pass a URL or a function as an action. When a function is passed as action, the function will handle the form submission. It is called with a single argument containing the form data of the submitted form. The action prop can also be overridden by a formAction attribute on a <button>, <input type="submit">, or <input type="image"> component.Let's see how it looks: function UncontrolledFormActionAttribute() { const handleSubmit = (formData) => { const name = formData.get('name'); alert(name); }; return ( <form action={handleSubmit}> <label>Enter your name: <br /> <input name="name" type="text" /> </label> <button type="submit">Submit</button> </form> ); } export default UncontrolledFormActionAttribute;Note: At the moment of writing this post, React’s extensions to <form> are currently only available in React’s canary and experimental channels.Which Approach to useWhen deciding between controlled and uncontrolled forms, consider the complexity and requirements of your application. Controlled forms are generally recommended for most use cases due to the precise control they offer over form data. They are particularly useful when you need to validate form input, control complex user interactions, or synchronize form state with other parts of your application. On the other hand, uncontrolled forms can be a better choice for simple forms or when integrating third-party libraries where direct access to form elements is needed. They are easier to set up and require less boilerplate code.For a balanced approach, you can use the FormFusion library, which combines the benefits of both methods. FormFusion uses the performant uncontrolled forms approach and takes the burden of validation, error handling, and other complexities off your hands. It also offers a controlled version using the useReducer hook under the hood, making it suitable for applications hat require more control over the form data.Ultimately, for large-scale applications that require more control over the form data, using FormFusion with its controlled version is preferable, while for simpler forms or when additional control over the form data is not required, the uncontrolled forms version provided by FormFusion might be a better choice.The code from this post can be found on Github. :root { --post-background-color: #7cb5ff;}
The Role of Input Masking in Web Forms
— Do & Learn
What is input masking Input masking is a software development practice used to format user input within form fields. It's a way to restrict how certain information is being typed into a field. This ensures that users follow a specific pattern, such as for phone numbers, dates, or credit card numbers and more. It prevents errors and makes data entry smoother, resulting in more accurate and consistent information.Purpose and Benefits Input masking serves two main purposes: improving data accuracy and enhancing user experience.Reduces errors By enforcing a specific format, input masking helps prevent users from entering data incorrectly, minimising typos and invalid entries.Improves user experience Input masking acts as a guide, automatically formatting data as the user types. This eliminates the need for users to remember specific formats and speeds up data entry.Improves data readability Additionally, a well-designed mask can visually organise the information, making it easier to read and review.Common Use CasesInput masking can be used in various scenarios. Such as:Formatting Personal Information This includes fields like: Phone numbers: Masks can ensure users include area codes, dashes, and proper formatting for their region. Social Security numbers or National Insurance numbers: Masks can automatically insert hyphens at the appropriate positions. Passport numbers or ID numbers: Masks can enforce specific lengths and alphanumeric character combinations. Financial Data Credit card numbers: Masks can enforce specific lengths and include spaces for better readability. Bank account numbers: Masks can ensure users enter the correct number of digits and any routing information required. Currency amounts: Masks can format numbers with commas and decimal places depending on the currency. Additionally masks can be used to append the suitable currency symbol. Dates and Times Masks can be used to: Standardize date formats: (e.g., MM/DD/YYYY, DD/MM/YYYY). Specify time formats: (e.g., 24-hour clock, 12-hour clock with AM/PM). IP Addresses Masks can ensure users enter the correct number of octets separated by periods.Postal/Zip Codes Masks can enforce the specific format for different countries or regions.Product Codes or Serial Numbers Masks can ensure that these codes/identifiers follow the internal tracking format.Accessibility Requirements Making sure everyone can benefit from input masking is very important. That means it should work well with tools such as screen readers that people might use. Developers need to make sure input masking doesn't create barriers for anyone by providing clear instructions and alternative ways to input data. Here are some suggestions to ensure everyone has a good user experience: Include a label with a for attribute related to the id of the input element. Use hint text that will help the users understand what they need to input. Make the helper text available to screen readers. This can be achieved by using accessibility attributes such as aria-describedby or aria-labeledby. Use the correct input field type that will allow users on a mobile device to see the proper keyboard. Avoid placing the mask format in the value property of the field. This might cause the screen reader to read it and cause confusion.Implementing input masking using FormFusion When it comes to input masking in React, FormFusion takes care of everything so that the developers can focus on other important things. The library offers built-in input masking functionality that can be easily applied to any input field by using the provided 'mask' property. If you're not familiar with FormFusion yet, make sure to checkout the documentation page first. Let's see it in action. Bellow is an example of how simple is to apply input masking on an input field in React using the FormFusion library (The full code and demo can be found on the links at the end of this post). <Form onSubmit={onSubmit}> <Input id="credit-card-number-hyphen" name="credit-card-number-hyphen" type="credit-card-number-hyphen" mask="####-####-####-####" /> <button type="submit">Submit</button> </Form> Here is how the result looks: When to avoid input masking Even though input masking can be useful when applied correctly, you should carefully decide when to use it since it can also lead to a poor user experience and frustration when used in the wrong scenario or if implemented incorrectly. Here are some use cases where using input masking might not be the best choice: When a free-form field is required The most obvious scenario is when there isn’t a common input pattern to use. For example: email address, full name, street address etc. These are the type of fields that the user's input should not be restriced and they should be allowed to enter their data freely. To prevent errors or incorrect data in these scenarios, validation checks should be performed.When the format is too complicated for a valid input mask Consider a situation where users need to enter vehicle identification numbers (VINs). VINs can vary in length and include both letters and numbers, often with specific patterns determined by the manufacturer. Trying to force a fixed input mask for VINs might confuse users, as they may struggle to fit their VIN into the required format. It's better to let users enter their VINs freely and then use validation checks to ensure accuracy before submitting the form.Alternatives In such cases where input masking is not suitable you can use a different approach and still provide a good user experience. For example: Format the data after the user completes the field: You can allow free-form input and after the user enters their information (e.g on blur), transform the data into the appropriate format. This way, it's easier for the user to input the information quickly and it is also displayed in a easy readable format. Avoid input masking entirely: Use well designed validation with user-friendly messages instead and make sure to display helpful labels and text that will guide the user to enter the information in the appropriate format.Conclusion Input masking is a useful tool for formatting data input which improves accuracy and user experience. However, its application requires careful consideration since it may not be suitable for every scenario. For example, with free-form input or complex data formats, alternative approaches like post-input formatting or robust validation checks may be more suitable. Prioritizing accessibility and user experience is important for effective integration of input masking without creating barriers for users. Using libraries such as FormFusion can provide you with a simple and easy way to correctly integrate input masking in your application.The code for the example shown in this post can be found on Github. The code and demo can be found on Stackblitz. :root { --post-background-color: #e95b4b;}
Client Side Validation Using FormFusion
— Do & Learn
Client-side validation is a fundamental aspect of web development, vital for delivering user-friendly interfaces and reinforcing the security of online platforms. It involves performing validation checks on the user’s device when filling out forms before sending the data to the server.Why is it important? 1. It provides instant feedback, making the user’s experience smoother and less error-prone. 2. Reduces server load by minimising unnecessary server requests for invalid data. 3. Enhances security by limiting what the user can input, thereby preventing malicious attacks like SQL injection or cross-site scripting. Validation types There are two different types of client-side validation that can be found on the web: 1. Built-in form validation: It uses HTML form validation features, requiring minimal Javascript. This results in better performance than JavaScript validation, although it is less customisable. 2. JavaScript validation: Coded using JavaScript, it offers complete customisation. However, it requires creating everything and may be less performant than built-in validation. This is where FormFusion comes into play. It leverages everything built-in form validation offers plus extends the list of available validation attributes that can be used. It offers 500+ validation types.How to use FormFusion for form validation Integrating FormFusion is straightforward. It offers already built components that are optimised with integrated validation logic, error handling and fully customisable look.The FormFusion's Input component builds upon the native HTML input element and enhances it with an expanded range of field types, each associated with a specific validation pattern. You can specify the field type by using the 'type' property. For example, let's say we need a form with one input field that should accept only alphabetic characters. Unlike other libraries where you will need to write a javascript function or a regex to achieve that, with FormFusion you only use the already provided and thoroughly tested field type 'alphabetic'. Here is how it looks: <Form onSubmit={onSubmit} className="form"> <Input name="name" type="alphabetic" /> <button type="submit">Submit</button> </Form> Now when the user enters characters in the input field that are not letters, they get the following message: This is the default look of FormFusion's form and input components since it is primarily unstyled library. To apply style to the form or input components you can use the same approach as with any html/jsx element, using the className property. Additionally, for the input component you can also pass a classes property to style the field, label and error elements directly: <Form onSubmit={onSubmit} className="form"> <Input name="username" type="alphabetic" label="Username" classes={{ field: 'input-field', label: 'input-field__label', error: 'input-field__error-message' }} /> <button type="submit">Submit</button> </Form> Here is how it looks: Next, let's say we don't like this default error message from FormFusion and we want to customise it. To do that, we can use the validation property and add our own custom message: <Form onSubmit={onSubmit} className="form"> <Input name="username" type="alphabetic" label="Username" classes={{ field: 'input-field', label: 'input-field__label', error: 'input-field__error-message' }} validation={{ patternMismatch: 'Please use letters only. Thank you!' }} /> <button type="submit">Submit</button> </Form> Here it is, our customised error message: FormFusion allows customising the error messages or all validity states an input element can be in. You can read more about this as well as see all available validation field types in FormFusion's documentation page. You can view and download the full code of this example on Github It’s important to note that client-side validation should not be viewed as a replacement for server-side validation. Server-side validation is also crucial and should always be used along with client-side validation since the network request can still be altered. :root { --post-background-color: #22c4d4;}
Exploring React’s Latest Extensions to Forms
— Do & Learn
React 19 brings exciting enhancements to form handling. It allows developers greater control over user interactions and smoother form management.Let’s outline the three key features: form now accepts a function passed as action prop. If a URL is passed, the form still behaves like the HTML form component but when a function is passed, it will handle the form submission. The function can be async and it’s called with a single argument containing the form data of the submitted form. useFormStatus hook: Provides status information of the last form submission. Developers can leverage this hook to dynamically respond to user input and provide real-time feedback. useFormState hook: Allows updating component state based on the result of a form action. Developers can use it to pass an existing form action function as well as an initial state and it returns new action along with the latest form state to be used. Integrating FormFusion with React’s Latest Updates: FormFusion (form management library) integrates with all of the React’s latest form features. React’s approach to handling a function as an action prop is the default method used in the FormFusion library via the onSubmit prop, however it also supports React’s approach out-of-the-box. Developers can use either and still get the same benefits. Let's set up a simple project to demonstrate how FormFusion integrates with React's form features. Begin by creating a new project. If you prefer to use an existing one, feel free to skip this step and proceed directly to step 2. For our example, we'll use Vite to quickly setup a new project. However, you can use any tool of your choice and follow steps 2 through 13 accordingly.1. Create a New Project with Vite First, make sure you have Node.js installed on your machine. Then, open your terminal and run the following commands to create a new project using Vite and the react template: npm create vite@latest my-form -- --template react 2. Install Dependencies Install the FormFusion library used in the form component: npm install formfusion 3. Create the Form Component In the project directory, create a new file named MyForm.jsx 4. Import Required Components Begin by importing the necessary components from the FormFusion library and any additional stylesheets. import { Form, Input } from 'formfusion'; import './App.css'; 5. Create a Functional Component Define a functional component named MyForm to encapsulate the form logic and UI. const MyForm = () => { // Form logic and UI will be implemented here }; 6. Define Form Submission Function Inside the MyForm component, define a function that will be called when the form is submitted. In this example, the search function extracts the search query from the form data and displays it in an alert. function search(formData) { const query = formData.get('query'); alert(`You searched for '${query}'`); } 7. Build the form UI Within the MyForm component, add the following code: return ( <Form action={search} validateOnChange className="form"> {/* Form content goes here */} </Form> ); 8. Define Input Field Use the FormFusion’s <Input> component inside the <Form> component to create a search input field. Customize the input field properties as needed, such as id, name, type, and placeholder. Additionally, you can specify CSS classes for styling purposes using the classes prop. <Input id="query" name="query" type="search" placeholder="Search for..." classes={{ field: 'form__input-field', error: 'form__input-field__error', label: 'form__input-field__label', }} /> 9. Add Submit Button Include a submit button within the form to trigger the form submission. <button type="submit">Submit</button> 10. Export Component Export the MyForm component as the default export to make it available for use in other parts of the application. export default MyForm; 11. Import the Form Component In the App.jsx file generated by Vite, import and use the MyForm component: import MyForm from './MyForm.jsx'; function App() { return ( <div> <MyForm /> </div> ); } export default App; Once you've implemented these steps, you'll have a functional form component (MyForm) that includes a search input field and a submit button. When the form is submitted, the search function will be called, extracting the search query from the form data and displaying it in an alert.12. Run the Project Start the development server to see the form in action. Run the following command in your terminal: npm run dev This command will start the Vite development server, and you should be able to access your project in the browser at the specified URL (usually http://localhost:3000 or http://localhost:5173).13. Test the Form Open your browser and navigate to the URL where your development server is running. You should see the form rendered on the page. Try entering some text into the search input field and submitting the form to see the alert message displaying the search query. You can find the full code on Github. While React’s form extension allows for client-side submissions, validation remains vital. FormFusion extends the form’s built-in validation by providing validation rules and intuitive error handling mechanisms. Developers can easily apply one of the 500+ validation rules FormFusion offers and bind them to form elements, ensuring data integrity and delivering user-friendly error messages. React 19’s form enhancements, combined with FormFusion’s advanced form management capabilities, open up new opportunities for developers. By leveraging FormFusion’s validation, submission on the client-side, and useFormStatus for real-time feedback, developers can build highly performant and user-friendly forms with ease. :root { --post-background-color: #73cbcf;}
Automating Component Generation with a Shell Script
— Do & Learn
React components are the building blocks of a React application. Creating them manually can be time-consuming, especially when they follow a similar structure. In this blog post, we'll explore how to automate the process of generating React components using a shell script. We'll build a script that takes a predefined template and creates components with custom names and file structure. *This example is only for Linux or MacOS Prerequisites Before we begin, ensure that you have the following set up: A working shell environment (e.g., Bash). Node.js and npm installed on your machine. Create a folder named template and create three files: index.js, component.css and interfaces.d.ts (if you're using typescript). If you are using scss or any other styling, create the style file according to your needs. Step 1: Setting up the Template First, let's set up a template file that represents the structure and content of a React component. For this simple example, we'll create a simple component that uses css for styling. // index.js const COMPONENT_NAME = () => { return ( <div> <h1>COMPONENT_NAME</h1> </div> ); }; export default COMPONENT_NAME; For the styling, create an empty component.css file. In the template, we use COMPONENT_NAME as a placeholder that will be replaced with the actual component name during the generation process. Step 2: Creating the Shell Script Now, let's create the shell script that will generate React components based on the template. Open a text editor and create a new file named generate-component.sh. Place the file in the root folder of your project or anywhere you want. For the example, we'll add the script in the root directory of an existing React project. Copy and paste the following code into the file: #!/bin/bash # Prompt the user to enter the path to the component template read -p "Enter the template path: " template # Prompt the user to enter the path where the component will be created read -p "Enter the destination where you want to create the new component: " dest # Prompt the user to enter the name of the component read -p "Enter the name of the component: " component_name The first step is to use prompt to get the necessary information about the component. The first line reads our input for the path where the template component is located. The second line, reads our input for the path where we want to create the new component, and the third line reads out input for the name of the new component. This can be case insensitive since later in the script we'll capitalize the component name. Next we'll check whether the template folder or destination folder given by us exist. If the destination folder i.e the new component location folder doesn't exist, it is created. Add the following code in the script: #!/bin/bash # Check if the template folder exists if [! -d "$template"]; then echo "Template folder '$template' does not exist." exit 1 fi # Check if the destination folder exists, if not, create it if [! -d "$dest/$component_name"]; then mkdir -p "$dest/$component_name" fi Next, what we want to do is copy all contents from the template folder to the newly created component folder. Add the following commands to the script: #!/bin/bash # Copy all contents from the template folder to the destination folder cp -R "$template"/* "$dest/$component_name" You should see the new folder created with the same files as the template. Next, we'll improve the script a bit and make all the renaming to customize the new component. First, we'll capitalize the component name and replace the placeholder with our new component name, and then we'll rename the component.css file to our component_name.css. If you're using scss or other, you'll need to adapt the script to use that file extention. #!/bin/bash # Capitalize the first letter of the component name first_letter = $(echo "${component_name:0:1}" | tr '[:lower:]' '[:upper:]') capitalized_name = "${first_letter}${component_name:1}" # rename all occurencies of COMPONENT_NAME with the capitalized component find "$dest/$component_name" -type f -exec sed -i '' -e "s/COMPONENT_NAME/$capitalized_name/g" { } + # rename the css file from component.css to $component_name.css mv "$dest/$component_name/component.css" "$dest/$component_name/$component_name.css" And finally, we'll print a success message that will tell us that the component is created successfully. #!/bin/bash echo "Component '$capitalized_name' created successfully!" Step 3: Running the Script To run the script, open a terminal, navigate to the directory where the script file is located, and execute the following command: sh ./generate-component.sh Note that you might need to change the permissions to run the script. To make the file executable you can run chmod +x generate-component.sh Once you run the script, it will prompt you to enter the template path, the location where you want the component to be created and the component name. If you want to make the generation quicker without option to enter the path names, you can adapt the script to use pre-defined locations for the template and the components folder. After entering the information, the script will generate a new component file based on the template, replacing the placeholder with the provided component name. Step 4: Generating components using npm in a React project This step covers incorporating the script in an existing React project and running it using npm. First, place the script we created in the previous steps in the React project where you want to automate component generation. You can add it either to the root directory, in a new scripts folder etc. Next, open the package.json file and scroll down to the "scripts" property. Add the following code in the scripts part: "generate-component": "sh ./generate-component.sh" You can also name the script however you like. Save the file and you should be able to run it by running npm run generate-component Conclusion In this blog post, we've explored how to create a shell script to automate the generation of React components using a predefined template. With this script, you can save time and effort by quickly generating components with a consistent structure. Feel free to enhance the script further based on your specific requirements, such as generating additional files (e.g., stylesheets) or adding more complex logic. :root { --post-background-color: #fef6ed;}
Share React Components Across Projects with Git Submodules
— Do & Learn
Reusing components and code is a fundamental aspect of React projects, offering developers the ability to write code once and leverage it across different parts of an application. But as projects evolve and grow, the need of reusing components and functionality across various React projects becomes increasingly apparent. There are a few ways and platforms that offer code sharing across projects (which I will mention later), however, in this post, we explore a simple concept of code-sharing between different React projects in a more private way by using Git submodules without relying on third-party platforms or tools. Git submodules are a feature in Git that allows you to integrate external repositories as dependencies within your project, maintaining their independence and version control. Section 1: Setting up Git Submodules for Component Sharing First, we'll need three different remote Git repositories. One will contain the code that we need to reuse - in this case a simple Button component, and the other two projects will be simple React apps with the Button from the shared repository. Create three empty remote repositories using the version control tool of your choice (for example Github) named: first-app, second-app and component-library. Then, clone the first-app and second-app locally and in each of its root directory run: git submodule add <repository_url> <submodule_path> where repository_url is the url of the repository you created that will contain the shared Button component, in this case component-library and submodule_path is the name of the directory that will contain the component-library repository, for this example, let's name it components. Section 2: Creating the shared component Next clone the component-library repository and in the root directory of the component library add a new folder button with index.js file for the component code and button.css for the styling: // index.js import './button.css'; function Button() { return ( <button className="button"> This is a Button component from my Component library </button> ); } export default Button; // button.css .button { background-color: #E8AA42; padding: 10px; border: 0; border-radius: 5px; box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); }Then, commit and push the changes to the remote: git add . git commit -m "create button component" git push Section 3: Using the Shared Components in the other React Project Now that we built the shared component, let's use it in two projects. First, we need to create the apps. For this simple idea, we'll demonstrate how to set up projects using different tools: Create React App, Vite, and Next.js.To create the first project, clone the first-app repository locally and navigate to its root directory. Using Create React App: To create the first project using Create React App, in the project directory run: npx create-react-app . Using Vite: If you prefer to use Vite, in the project directory run the following command: npm create vite@latest . --template react Using Next.js: For a Next.js app, in the project directory run the following command: npx create-next-app . If you wish to use a different tool to build the app, please refer to their official documentation to get specific instructions on setting up your project.Since we already setup the submodule, we need to update it to get the latest changes we did. cd src/components git pullTo use the shared component, open the App.js file and import the Button component from the src/components/button folder. Start the application with npm run start and test the changes! You should be able to see the button component. Repeat the same process with the second-app repository.Section 4: Managing Changes and UpdatesChanges and updates can be done from any repository that uses the submodule or the submodule itself. But note that after some changes are made, you'll need to pull the changes in the repository where you'll need them before making other changes. This is maybe one of the biggest disadvantages of using git submodules instead of code-sharing platforms, however the purpose of this post is to explain how to share code with minimum configuration while keeping your code in your private repositories without using third-party platforms. If this doesn't fit your situation you can use other tools and platforms such as Bit.de, Storybook alongside Chromatic, Styleguidist and others.Section 5: Testing shared components in isolation (Optional)If you also want to view and test the shared components in isolation, then you can do that by adding a little more configuration.For this, we'll need to use a build tool that will create the environment where we'll test the shared components. There are a few options here, but this example will use Parcel as it's light and quick and easy to setup. You can also use any other build tool of your choice, whichever best fits your scenario. First, open the component-library project and in the root directory, create the following package.json file: { "name": "component-library", "source": "index.html", "scripts": { "start": "parcel", "build": "parcel build" }, "devDependencies": { "parcel": "latest" } } Then run the following to install parcel: npm install --save-dev parcel Next, since we're working with React projects we need to install react and react-dom: npm install react react-dom Create the entry index.html file and paste the following code: <html lang="en"> <head> <meta charset="utf-8" /> <title>My Component Library</title> </head> <body> <div id="app"></div> <script type="module" src="index.js"></script> </body> </html> Create an index.js file and paste the following code: import { createRoot } from "react-dom/client"; import { App } from "./App"; const container = document.getElementById("app"); const root = createRoot(container) root.render(<App />); And finally create the App.js file with the Button component that we want to test: import Button from './button'; export function App() { return <Button />; } To run the application run: npm run start and to build run: npm run buildThese commands will build the application and write the output in dist directory.You can then open the local server on http://localhost:1234 and you should see the Button component. While this post does not cover the details of deploying projects, once you have successfully set up the code-sharing mechanism, you can proceed with deploying and publishing the component library on your preferred server. By doing so, you gain the ability to preview and test the shared components at any time. Section 6: AlternativesSome other alternatives that provide similars value are: Bit.dev - a platform that facilitates component-driven development and component sharing across projects. It provides a centralized hub for developers to discover, share, and collaborate on reusable components. With Bit.dev, you can publish and version components, manage their dependencies, and integrate them into different projects, regardless of the framework or tooling being used. It offers features like component documentation, testing, and a package manager-like experience for sharing and consuming components. Storybook - a popular open-source tool for developing UI components in isolation. It allows you to build and organize a library of components, view them in different states, and document their usage. Storybook supports various frameworks like React, Vue, Angular, and more. Styleguidist- a documentation tool specifically designed for React component libraries. It helps you create living style guides and documentation by providing an interactive environment where you can showcase and test your components. Chromatic -a tool that focuses on visual testing and UI review for component-driven development. It integrates with popular component frameworks like React, Angular, and Vue, and helps you catch UI bugs, perform visual regression testing, and collaborate with team members on component changes. Conclusion: While Git submodules offer a straightforward approach to code sharing, it's important to note that this approach may not be suitable for every scenario. Like any other tools and platforms, it has its own set of advantages and disadvantages. Therefore, it is crucial to thoroughly research and evaluate your specific needs before deciding on the best option for your project. Consider factors such as the complexity of your codebase, the scale of your projects, the level of collaboration required, and the desired level of control over dependencies. Additionally, take into account the learning curve, maintenance overhead, and compatibility with your existing development workflow. Remember, choosing the right approach is essential for long-term productivity and success. Github examples: UI Library First App Second App :root { --post-background-color: #07283f; --post-content-color: #ffffff;}
How Junior Developers and Interns Can Impress from Day One
— professional growth
Starting a new job as a junior developer or intern in a software company can be an overwhelming and intimidating experience. Everything may seem confusing and complicated at first. Even after the initial onboarding process, you may still be left with a lot of unknown variables. However, in most cases, you get about three or four weeks before you’re expected to have integrated into your work community. So use that opportunity and give yourself time to transition and make the process a little more pleasant while also leaving a good lasting impression. As a Team Lead with years of experience and also being a new junior developer myself at some point, I've noticed some common "mistakes" that juniors do (or don't do) and have put together some tips to help you avoid them. Not getting familiar enough with the company’s technology stack and project/s Most of the time you’ll be given time to get familiar with the technology stack that is being used, projects that they’re working on, and probably documentation and coding guidelines.It is essential to use that time efficiently and get thoroughly familiar with it. Of course, it won’t be expected of you to understand the codebase from day one, but what is important here is taking the time to read their guidelines. If there are no guidelines available, try to glean insight from the codebase and version control history, and don't hesitate to ask your colleagues. They’ve been there longer than you and can provide you with valuable information about the codebase and the company’s development practices. I’m sure that almost everyone will be glad to explain to you any questions you might have. It is also a great opportunity to bond with them. Starting to code without fully understanding the requirements Once you get familiar with the company’s workflow, you’ll be given tasks to complete. Some of the most common pitfalls I've noticed among juniors is that they start working on a task without completely understanding the requirements. While task requirements may not always be clearly specified, it's crucial to pay close attention to them and read them multiple times if necessary until you’re 110% sure that you understand what needs to be done. If something is not clear enough, ask your colleagues for clarification. Never assume; always ask. Providing the first solution that comes to mindAnother mistake is starting to code the first solution that comes to mind without considering other options. While there may be deadlines to meet, it's best to consider multiple solutions to a problem and find the one that best fits the given situation. I believe that the first few days/weeks you won’t be given any critical tasks, so here is an opportunity to make a good and professional impression even though you don’t have years of experience. Overlooking the “minor" things This is something that can be interpreted in a lot of different ways depending on the situation. But since I’m giving my personal opinions from a more technical point of view, this is what the “minor” things include: Not paying attention to code formattingThis is something that most juniors/interns don’t pay attention to because it doesn’t seem very important at first but it can be an opportunity for leaving a good impression. If you’re not sure how it is supposed to be, do the same that is already done. Always match the existing formatting style instead of enforcing your own. Not following the established style guidelines and naming standards Since you’ll probably be working on an existing project, make sure to always follow their established standards for styling, naming variables/functions/files, etc. There is no need to reinvent anything. Not giving an effort in documenting your work as clearly and descriptively as possible The company will probably be using some form of versioning control system, so you’ll need to document your changes. Make sure to provide clear and detailed information about the changes you’re introducing. This will make you seem more professional and help the code reviewer understand your changes better. Quality documentation will also help you better understand the work that you’re doing. Relying too heavily on ChatGPT While ChatGPT can be helpful for coding solutions and questions, I personally don't recommend relying too much upon and overusing it for your problems. It doesn't help you develop your programming logic. At this stage, you’re supposed to be learning how to learn and gain a better understanding in your own way by using your logic and creativity instead.Of course, there are exceptions to this. You will not always need to invent something. A lot of things are already done, and you can save a lot of time by finding the correct thing - library/solution/tool/piece of code, etc. and using it. However, I still recommend researching these types of things on your own from multiple sources. Sure, use ChatGPT (or any other AI) as well, but don’t rely on it as the ultimate source. :root { --post-background-color: #f1f1f1;}