Skip to content

Taming Forms With react-hook-form

Taming Forms With react-hook-form

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

Taming Forms With react-hook-form

After some time of doing repetitive tasks like handling forms in React.js, you will notice that there is a lot of boilerplate code that can be abstracted into reusable custom hooks. Luckily, there is plenty of existing Open Source solutions. In this case, we will be using react-hook-form.

What is react-hook-form

react-hook-form is a performant, flexible, and extensible form handling library built for React. It exports a custom hook that you can call within your Functional Components, and returns both a register function that you pass as a ref to your input components, and a handleSubmit function to wrap your submit callback.

By returning a register function that will be added to the input component, we can leverage the Uncontrolled Component pattern to make our application faster and more performant, by avoiding unnecessary re-renders.

What are we going to build?

To get a better understanding of what react-hook-form can do, we will build a simple application showing a list of characters and a form to add them to our list.

Screen-Recording-2020-10-19-at-1

Application Setup

Before going right into react-hook-form, we will need to prepare our application with the basic file structure and functionality. For this, we will create a new react application (you can either use your preferred starter or cloud IDE).

If you want to skip the application setup, you can go ahead and fork this CodeSandbox, but I highly recommend you at least read this section to have a better understanding of what the app does.

1. Characters List

Let's start by creating a new component where we will display our characters.

character-list.js

import React from "react";

function CharacterList({ characters }) {
  return (
    <div>
      <h2>Character List</h2>

      {characters.length === 0 ? (
        <p>
          <em>Your character list is empty</em>
        </p>
      ) : (
        <ul>
          {characters.map((character, id) => (
            <li key={id}>
              {character.name} (<strong>{character.species}</strong>)
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default CharacterList;

If you have a basic understanding of React, you will notice our CharacterList component will receive a characters prop, which is an array of objects with the properties name and species. If the array is empty, we will render a placeholder. Elsewhere, we will render the list.

2. Add Character Form

The second step is to create a component that will render the form to add a new character to our list.

character-form.js

import React from "react";

function CharacterForm({ addCharacter }) {
  const onSubmit = (data) => {
    addCharacter(data);
  };

  return (
    <div>
      <h2>Add character</h2>
      <form onSubmit={onSubmit}>
        <div>
          <input name="name" placeholder="Character name" />
        </div>
        <div>
          <select name="species">
            <option value="sorcerer">Sorcerer</option>
            <option value="knight">Knight</option>
            <option value="human">Human</option>
          </select>
        </div>
        <div>
          <button type="submit">Add character</button>
        </div>
      </form>
    </div>
  );
}

export default CharacterForm;

By itself, this component won't do anything because we are not doing anything with the collected data, nor validating our fields. This will be the component where we will be working on the next section of this tutorial.

3. The App

Now, let's just create the App component where we will render CharacterList and CharacterForm.

app.js

import React from "react";

import CharacterList from "./character-list";
import CharacterForm from "./character-form";

function App() {
  const [characters, setCharacters] = React.useState([]);

  const addCharacter = (character) => {
    setCharacters((characters) => [...characters, character]);
  };

  return (
    <div>
      <CharacterList characters={characters} />
      <hr />
      <CharacterForm addCharacter={addCharacter} />
    </div>
  );
}

export default App;

We will be saving our character list in characters by using the React.useState hook, and passing them down to CharacterList. Also, we created an addCharacter function that will just add a new character at the end of the characters list, and pass it to CharacterForm via prop.

Let's get to it!

Now that we have our application setup, let's see how can we leverage react-hook-form to take our forms to the next level.

Install react-hook-form

yarn add react-hook-form

Add react-hook-form to your CharacterForm

Here comes the fun. First, let's import useForm from react-hook-form, call the hook in our component, destructure register and handleSubmit methods out of it (don't worry, I will explain what they do just in a while), wrap our onSubmit function with handleSubmit, and pass register as the ref for each one of our form controls.

character-form.js

import React from "react";
+import { useForm } from "react-hook-form";

function CharacterForm({ addCharacter }) {
+  const { register, handleSubmit } = useForm();
+
-  const onSubmit = (data) => {
-    addCharacter(data);
-  };
+  const onSubmit = handleSubmit((data) => {
+    addCharacter(data);
+  });

  return (
    <div>
      <h2>Add character</h2>
      <form onSubmit={onSubmit}>
        <div>
-          <input name="name" placeholder="Character name" />
+          <input ref={register} name="name" placeholder="Character name" />
        </div>
        <div>
-          <select name="species">
+          <select ref={register} name="species">
            <option value="sorcerer">Sorcerer</option>
            <option value="knight">Knight</option>
            <option value="human">Human</option>
          </select>
        </div>
        <div>
          <button type="submit">Add character</button>
        </div>
      </form>
    </div>
  );
}

export default CharacterForm;

The register method

By attaching the register ref to our form controls, we can start tracking some stuff like the field value, its validation status, and even if the field had been touched or not.

Important: the name prop is required when passing the register ref, and it should be unique. This way, react-hook-form will know where to assign the field value. For more information, check out the register documentation.

The handleSubmit method

This is a function that wraps our submit callback, and passes the actual form values to it. Under the hood, it also calls preventDefault on the form event to avoid full page reloads. It can also be an asynchronous function.

For more information, check out the handleSubmit documentation.

Add some validations

At this point, we have a working form that is able to add characters to our list. However, we are not checking if the field is filled, in order to avoid empty submissions.

With react-hook-form, it is as simple as calling the register function with a configuration object defining the validation rules. For our case, we will make the name field required. Also, we can extract errors from useForm to show the user if the field has errors.

import React from "react";
import { useForm } from "react-hook-form";

function CharacterForm({ addCharacter }) {
-  const { register, handleSubmit } = useForm();
+  const { register, handleSubmit, errors } = useForm();

  const onSubmit = handleSubmit((data) => {
    addCharacter(data);
  });

  return (
    <div>
      <h2>Add character</h2>
      <form onSubmit={onSubmit}>
        <div>
-          <input ref={register} name="name" placeholder="Character name" />
+          <input
+            ref={register({ required: true })}
+            name="name"
+            placeholder="Character name"
+          />
+          {errors.name && errors.name.type === "required"
+            ? "Name is required"
+            : null}
        </div>
        <div>
          <select ref={register} name="species">
            <option value="sorcerer">Sorcerer</option>
            <option value="knight">Knight</option>
            <option value="human">Human</option>
          </select>
        </div>
        <div>
          <button type="submit">Add character</button>
        </div>
      </form>
    </div>
  );
}

export default CharacterForm;

Reset the form status

The final step is to clear our form after successfully adding a character to our character list. For that, we will destructure a new method from the useForm hook: reset, and call it after addCharacter.

import React from "react";
import { useForm } from "react-hook-form";

function CharacterForm({ addCharacter }) {
-  const { register, handleSubmit, errors } = useForm();
+  const { register, handleSubmit, errors, reset } = useForm();

  const onSubmit = handleSubmit((data) => {
    addCharacter(data);
+    reset();
  });

  console.log(errors.nameRequired);

  return (
    <div>
      <h2>Add character</h2>
      <form onSubmit={onSubmit}>
        <div>
          <input
            ref={register({ required: true })}
            name="name"
            placeholder="Character name"
          />
          {errors.name && errors.name.type === "required"
            ? "Name is required"
            : null}
        </div>
        <div>
          <select ref={register} name="species">
            <option value="sorcerer">Sorcerer</option>
            <option value="knight">Knight</option>
            <option value="human">Human</option>
          </select>
        </div>
        <div>
          <button type="submit">Add character</button>
        </div>
      </form>
    </div>
  );
}

export default CharacterForm;

For more information, check out the reset documentation.

Moving forward

Now that you have a better sense of how to manage your React forms, you have unlocked a new world of possibilities by using battle-tested and community-validated libraries like react-hook-form.

You can take a look at more advanced use cases, additional resources or even take a look at the full API.

If you want a finished code sample, you can check out this CodeSandbox.

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

You might also like

How to test React custom hooks and components with Vitest cover image

How to test React custom hooks and components with Vitest

Introduction In this guide, we'll navigate through the process of testing React hooks and components using Vitest—a powerful JavaScript unit testing framework. Discover how Vitest simplifies testing setups, providing an optimal solution for both Vite-powered projects and beyond. Vitest is a javascript unit testing framework that aims to position itself as the Test Runner of choice for Vite projects and as a solid alternative even for projects not using Vite. Vitest was built primarily for Vite-powered projects, to help reduce the complexity of setting up testing with other testing frameworks like Jest. Vitest uses the same configuration of your App (through vite.config.js), sharing a common transformation pipeline during dev, build, and test time. Prerequisites This article assumes a solid understanding of React and frontend unit testing. Familiarity with tools like React Testing Library and JSDOM will enhance your grasp of the testing process with Vitest. Installation and configuration Let’s see how we can use Vitest for testing React custom hooks and components. But first, we will need to create a new project with Vite! If you already have an existing project, you can skip this step. ` Follow the prompts to create a new React project successfully. For testing, we need the following dependencies installed: Vitest as the unit testing framework JSDOM as the DOM environment for running our tests React Testing Library as the React testing utilities. To do so, we run the following command: ` Once we have those packages installed, we need to configure the vite.config.js file to run tests. By default, some of the extra configs we need to set up Vitest are not available in the Vite config types, so we will need the vite.config.ts file to reference Vitest types by adding /// reference types=”vitest” /> at the top of the file. Add the following code to the vite.config.ts ` We set globals to true because, by default, Vitest does not provide global APIs for explicitness. So with this set to true, we can use keywords like describe, test and it without needing to import them. To get TypeScript working with the global APIs, add vitest/globals to the types field in your tsconfig.json. ` The environment property tells Vitest which environment to run the test. We are using jsdom as the environment. The root property tells Vitest the root folder from where it should start looking for test files. We should add a script for running the test in package.json ` With all that configured, we can now start writing unit tests for customs hooks and React components. Writing test for custom hooks Let’s write a test for a simple useCounter hook that takes an initial value and returns the value, an increment function and a decrement function. ` We can write a test to check the default return values of the hook for value as below: ` To test if the hook works when we increment the value, we can use the act() method from @testing-library/react to simulate the increment function, as shown in the below test case: ` Kindly Note that you can't destructure the reactive properties of the result.current instance, or they will lose their reactivity. Testing hooks with asynchronous logic Now let’s test a more complex logic that contains asynchronous logic. Let’s write a useProducts hook that fetches data from an external api and return that value ` Now, let’s see what the test looks like: ` In the above example, we had to spy on the global fetch API, so that we can mock its return value. We wrapped that inside a beforeAll so that this runs before any test in this file. Then we added an afterAll method and called the mockRestore() to run after all test cases have been completed and return all mock implementations to their original function. We can also use the mockClear() method to clear all the mock's information, such as the number of calls and the mock's results. This method is handy when mocking the same function with different return values for different tests. We usually use mockClear() in beforeEach() or afterEach() methods to ensure our test is isolated completely. Then in our test case, we used a waitFor(), to wait for the return value to be resolved. Writing test for components Like Jest, Vitest provides assertion methods (matchers) to use with the expect methods for asserting values, but to test DOM elements easily, we will need to make use of custom matchers such as toBeInTheDocument() or toHaveTextContent(). Luckily the Vitest API is mostly compatible with the Jest API, making it possible to reuse many tools originally built for Jest. For such methods, we can install the @testing-library/jest-dom package and extend the expect method from Vitest to include the assertion methods in matchers from this package. ` After installing the jest-dom testing library package, create a file named vitest-setup.ts on the root of the project and import the following into the project to extend js-dom custom matchers: ` Since we are using typescript, we also need to include our setup file in our tsconfig.json: ` In vite.config.ts, we need to add the vitest-setup.ts file to the test.setupFiles field: ` Now let’s test the Products.tsx component: ` We start by spying and mocking the useProducts hook with vi.spyOn() method from Vitest: ` Now, we render the Products component using the render method from @testing-library/react and assert that the component renders the list of products as expected and also the product has the title as follows: ` In the above code, we use the render method from @testing-library/react to render the component and this returns some useful methods we can use to extract information from the component like getByTestId and getByText. The getByTestId method will retrieve the element whose data-testid attribute value equals product-list, and we can then assert its children to equal the length of our mocked items array. Using data-testid attribute values is a good practice for identifying a DOM element for testing purposes and avoiding affecting the component's implementation in production and tests. We also used the getByText method to find a text in the rendered component. We were able to call the toBeInTheDocument() because we extended the matchers to work with Vitest earlier. Here is what the full test looks like: ` Conclusion In this article, we delved into the world of testing React hooks and components using Vitest, a versatile JavaScript unit testing framework. We walked through the installation and configuration process, ensuring compatibility with React, JSDOM, and React Testing Library. The comprehensive guide covered writing tests for custom hooks, including handling asynchronous logic, and testing React components, leveraging custom matchers for DOM assertions. By adopting Vitest, developers can streamline the testing process for their React applications, whether powered by Vite or not. The framework's seamless integration with Vite projects simplifies the setup, reducing the complexities associated with other testing tools like Jest....

React Conf 2024 Review cover image

React Conf 2024 Review

A lot has happened since the last React Conf, so 2024 was action-packed. The conference lasted two days in Las Vegas and was focused on all the new stuff coming with React v19. Day one was centered around React 19/Server Components, and day two was React Native. There were some exciting reveals, fantastic talks, and demos. This post will cover the conference's big news, specifically the main keynotes from days 1 and 2. Day 1 Keynote The main keynote included several speakers from the React team, and early in the talk they mentioned a goal of “Make it easy for anyone to build great user experiences.” This sounds great but I was curious to see exactly what they meant by this since it seems like React has arguably gotten more complex with the introduction of Server Components and related API’s. The keynote was split into 3 sections: State of React - Lauren Tan Lauren shared the staggering adoption React has received over the years and thanked all the contributors and the community. She also highlighted the amazing ecosystem of packages, tools, and frameworks. It’s hard to argue against these points, but React seems to be in a weird in-between state. The framework is headed in this new direction but doesn’t feel quite there yet. React 19 A few team members collaborated on a walkthrough of React 19, which included all the new features and a bunch of quality-of-life improvements. The main highlights were: * Server Rendering & Suspense - One of the coolest bits was showing how to co-locate and load stylesheets and other assets in components. “HTML Composition for Bundlers, Libraries, & Apps” * Hydration - This was a fan favorite, and the TL;DR is fewer hydration errors with better messages and debuggability. * Actions - This is one of my favorite new features, as it feels like something that was missing from the React toolkit for a long time. The new Actions API is a “First-class pattern for asynchronous data updates in response to user input”. It includes Remix inspired form behaviors and API’s that replace a lot of boilerplate for handling async operations like loading and error states. * JSX Improvements One of the most applauded announcements was the changes to refs, which are now automatically passed to every component 🙌 React Compiler !! Yes, we’ve all heard the news by now. They are finally open sourcing the compiler that they teased over 2 years ago. React compiler understands React code/rules and is able to automatically optimize/memoize our application code. One of the big complaints of React has long been that there’s a lot of mental overhead and thought that needs to be put into optimizing and building fast components. So, the goal is to remove that burden from the developers so that they can focus on writing their business logic. One of the slides mentioned that the compiler has the following behaviors: Dead Code Elimination, Constant Propagation, Type Inference, Alias Analysis, Mutable Lifetime Inference, HIR, and Diagnostics. I’m not going to pretend to know all those things, but I’ve already heard a lot of positive feedback about how it works and how advanced it is. The team mentioned that they are already running the compiler in production on Instagram.com and some of their other websites. Day 2 Keynote Day 2 opened with sharing the incredible growth in downloads and adoptions that React Native has had (78M downloads in 2023). They shared some meta info about the recent releases and talked about all the partners helping to push the framework forward. They mentioned the Meta investment in React Native and shared about some of the ways that they are using it internally in places like Facebook Marketplace. Next they shared about all the work that Microsoft is doing with React Native for things like supporting building Windows apps with React Native. I was surprised to learn that the Windows Start Bar is a React Native app 🤯. New Architecture "Rewrite React Native to be synchronous so it’s possible to be concurrent" I was surprised when I read this since these two things seem to conflict with one another. The new architecture allows for the concurrent mode in React. This section opened by describing the original architecture of React Native (JavaScript code with a bridge for passing information back and forth from the native layer) The new architecture replaces “the bridge” with a concept called “JSI (JavaScript Interface)”. Instead of message passing, it uses memory sharing between the JS and Native layers. The big announcement was that the new architecture has been moved into Beta and they have released a bunch of things to help with compatibility between the old and new architecture to minimize breakages when upgrading. Production Ready Apps The section opened by talking about all the complexities and pieces involved with shipping a high-quality app to Android and iOS. If you’re a mobile developer, you know it’s quite a lot. They highlighted a new section of the documentation that provides details about “React Native Frameworks”. They are recommending using a “Framework” with React Native, just like they are recommending for React on the web. I had never even considered the idea of a “React Native Framework” before, so I thought that was pretty surprising. I guess it makes sense that Expo is a Framework but I’m not aware of any others like it. So the recommendation from the React Native documentation is: “Use Expo”. Which leads to the next part of the keynote…. Expo This part of the talk went more in-depth on a lot of the complexities with mobile development that Expo helps you solve. They highlighted an amazing suite of modules that they have dubbed the “Expo SDK”. The SDK consists of well-defined modules that handle some type of integration with the underlying Native platforms. They also have a Modules API to help build your own Native modules. Another new feature that I wasn’t aware of is API routes with Expo Router. How does that even work??? The next thing they mentioned was a feature called CNG (Continuous Native Generation). I’ve played with this a bit and it’s pretty nice. Expo will generate the iOS and Android projects/code that is needed to build the final application. The sales pitch is that this will help with upgrades if you aren’t managing and changing your native code by yourself. If you’ve upgraded a React Native app before you know how painful this can be. I am interested to see if CNG is practical in the real world. In my experience most projects have ended up ejected from expo and require managing your own native code. This section wrapped up with talking about builds and deployments with Expo Application Services (EAS). I don’t have experience with EAS yet personally but builds and deployments have always been the biggest pain point in my experience. I would definitely give it a try on my next project. As someone who has worked with React Native and Expo quite a bit over the years, it truly has come a really long way. Expo is amazing. Summary There were a ton of really exciting announcements and content just between the two keynotes. My impression from the conference is that a new era of React is kicking off and it’s really exciting. I’d hate to be one of the people betting against React as they’ve shown time and time again that they are willing to take risks and innovate in this space. It’s going to be a busy year in the React ecosystem as more frameworks adopt Server Components and more applications give it a try. There were a bunch of other really incredible talks in the conference and I’m really excited to write about a couple of them in particular. I wish I could have attended in person but the live stream didn’t disappoint!...

What's New in Next.js cover image

What's New in Next.js

What's new in Next.js On October 27th 2020, the first ever Next.js Conf streamed to an audience of more than 34K. Let's go over the most important highlights! Next.js v10 The new version of Vercel's framework was released, boasting exciting new features that directly affect website performance and Web Vitals Metrics, allowing developers to have a better impact in search results. So what are these features? New Image Component One of the core metrics of the Web Vitals is the Largest Contentful Paint (LCP), and it directly affects user experience as a web application loads. Often, images are not properly optimized to have different behaviour based on the visitor device. For example, you may be downloading a 1000x1000 pixels image and only rendering a 200x200 pixels image on mobile devices. Luckily, this new version introduces a new Image component that acts as a drop-in replacement for the HTML element, with the advantage that comes with built-in optimization for different devices. Under the hood, images rendered with the new Image compoonent will be lazy-loaded, and will only render when they are within the viewport. Migration Integrating the new Image component is as easy as replacing all img elements with the new component: *Before* ` *After* ` One important thing to note is that the width and height props are required. Although one may think it will affect responsive layouts, this is not the case, because the image will be automatically made responsive based on the aspect ratio from the provided dimensions. Internationalization After 2 months of gathering feedback from the community, in an RFC, the ability to add i18n to a Next.js application is now built into the framework. Before this release, it was only possible through a custom server, which is not encouraged anymore by the Vercel team. Instead, you can now easily configure a Next.js application to have i18n capabilities by just adding some extra parameters to the configuration file: next.config.js ` The routing can happen at two different levels; subpath routing (adds the locale as an URL path, i.e. https://miwebsite.com/es/about) and domain routing (reads the locale from an absolute URL, i.e https://mywebsite.es). To enable domain routing, the configuration file will need to be slightly different: next.config.js ` This new version also has automatic language detection on the / route by checking the Accept-Language header, which will be matched against the provided configuration. Improved Link component Trying to create a link to a dynamic route by using a combination of the href and as props was somewhat confusing before. However, now the next/link component only needs the href prop. *Before:* ` *After:* ` This is not a breaking change so you can still continue using both props. However, now it is more developer-friendly. Blocking fallback for getStaticPaths What makes Next.js stands over other frameworks is the ability to incrementally generate static pages. This means, if you are building an e-commerce platform with *lots* of products, you won't need to statically generate all pages at build time. Instead, you can opt in to incrementally generate the pages in getStaticPaths. Starting in Next.js 9.3, you could opt-in to this by adding a fallback property in getStaticPaths. At build time, a static fallback page was generated so the first time a user visited a given URL, it would be served, and once the data was fetched, a new page would be served, and pushed to the CDN for subsequent loads. However, now we have the ability to avoid showing a fallback page at all and instead blocking the render until the data has been fetched by just adding fallback: 'blocking' to the returned object from getStaticPaths. *Before:* ` *After:* ` notFound support for getStaticProps and getServerSideProps The getStaticProps and getServerSideProps methods implemented a new boolean property: notFound. By adding it, your users will be redirected to your 404 page. ` redirect support for getStaticProps and getServerSideProps A redirect property was also added to both methods. Allowing you to redirect to either an internal or an external page, with the ability to also mark the redirect as permanent. ` Vercel's Next.js Analytics Although this is not related only with the framework, Vercel released Next.js Analytics, a new feature of the platform that collects analytics from real users by measuring Core Web Vitals and generating reports. Next.js Commerce If you are planning to spin up an e-commerce functionality on top of Next.js, you can just clone an all-in-one starter kit that includes all of the previously mentioned new features. Moving forward We are planning to release a series of Next.js articles in the upcoming weeks, including in-depth guides on how to create a Next.js application using all of the new features, including best practices and tips from the community. Stay tuned!...

What Sets the Best Autonomous Coding Agents Apart? cover image

What Sets the Best Autonomous Coding Agents Apart?

Must-have Features of Coding Agents Autonomous coding agents are no longer experimental, they are becoming an integral part of modern development workflows, redefining how software is built and maintained. As models become more capable, agents have become easier to produce, leading to an explosion of options with varying depth and utility. Drawing insights from our experience using many agents, let's delve into the features that you'll absolutely want to get the best results. 1. Customizable System Prompts Custom agent modes, or roles, allow engineers to tailor the outputs to the desired results of their task. For instance, an agent can be set to operate in a "planning mode" focused on outlining development steps and gathering requirements, a "coding mode" optimized for generating and testing code, or a "documentation mode" emphasizing clarity and completeness of written artifacts. You might start with the off-the-shelf planning prompt, but you'll quickly want your own tailored version. Regardless of which modes are included out of the box, the ability to customize and extend them is critical. Agents must adapt to your unique workflows and prioritize what's important to your project. Without this flexibility, even well-designed defaults can fall short in real-world use. Engineers have preferences, and projects contain existing work. The best agents offer ways to communicate these preferences and decisions effectively. For example, 'pnpm' instead of 'npm' for package management, requiring the agent to seek root causes rather than offer temporary workarounds, or mandating that tests and linting must pass before a task is marked complete. Rules are a layer of control to accomplish this. Rules reinforce technical standards but also shape agent behavior to reflect project priorities and cultural norms. They inform the agent across contexts, think constraints, preferences, or directives that apply regardless of the task. Rules can encode things like style guidelines, risk tolerances, or communication boundaries. By shaping how the agent reasons and responds, rules ensure consistent alignment with desired outcomes. Roo code is an agent that makes great use of custom modes, and rules are ubiquitous across coding agents. These features form a meta-agent framework that allows engineers to construct the most effective agent for their unique project and workflow details. 2. Usage-based Pricing The best agents provide as much relevant information as possible to the model. They give transparency and control over what information is sent. This allows engineers to leverage their knowledge of the project to improve results. Being liberal with relevant information to the models is more expensive however, it also significantly improves results. The pricing model of some agents prioritizes fixed, predictable costs that include model fees. This creates an incentive to minimize the amount of information sent to the model in order to control costs. To get the most out of these tools, you’ve got to get the most out of models, which typically implies usage-based pricing. 3. Autonomous Workflows The way we accomplish work has phases. For example, creating tests and then making them pass, creating diagrams or plans, or reviewing work before submitting PRs. The best agents have mechanisms to facilitate these phases in an autonomous way. For the best results, each phase should have full use of a context window without watering down the main session's context. This should leverage your custom modes, which excel at each phase of your workflow. 4. Working in the Background The best agents are more effective at producing desired results and thus are able to be more autonomous. As agents become more autonomous, the ability to work in the background or work on multiple tasks at once becomes increasingly necessary to unlock their full potential. Agents that leverage local or cloud containers to perform work independently of IDEs or working copies on an engineer's machine further increase their utility. This allows engineers to focus on drafting plans and reviewing proposed changes, ultimately to work toward managing multiple tasks at once, overseeing their agent-powered workflows as if guiding a team. 5. Integrations with your Tools The Model Context Protocol (MCP) serves as a standardized interface, allowing agents to interact with your tools and data sources. The best agents seamlessly integrate with the platforms that engineers rely on, such as Confluence for documentation, Jira for tasks, and GitHub for source control and pull requests. These integrations ensure the agent can participate meaningfully across the full software development lifecycle. 6. Support for Multiple Model Providers Reliance on a single AI provider can be limiting. Top-tier agents support multiple providers, allowing teams to choose the best models for specific tasks. This flexibility enhances performance, the ability to use the latest and greatest, and also safeguards against potential downtimes or vendor-specific issues. Final Thoughts Selecting the right autonomous coding agent is a strategic decision. By prioritizing the features mentioned, technology leaders can adopt agents that can be tuned for their team's success. Tuning agents to projects and teams takes time, as does configuring the plumbing to integrate well with other systems. However, unlocking massive productivity gains is worth the squeeze. Models will become better and better, and the best agents capitalize on these improvements with little to no added effort. Set your organization and teams up to tap into the power of AI-enhanced engineering, and be more effective and more competitive....

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co