{"id":250718,"date":"2024-09-26T03:00:23","date_gmt":"2024-09-26T10:00:23","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/visualstudio\/?p=250718"},"modified":"2024-09-30T16:55:32","modified_gmt":"2024-09-30T23:55:32","slug":"creating-a-react-todo-app-in-visual-studio-2022","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/visualstudio\/creating-a-react-todo-app-in-visual-studio-2022\/","title":{"rendered":"Creating a React TODO app in Visual Studio 2022"},"content":{"rendered":"<p>In this tutorial we will create a React front-end, with JavaScript, of a TODO web app using a Visual Studio 2022. To get started <a href=\"https:\/\/learn.microsoft.com\/en-us\/visualstudio\/install\/install-visual-studio?view=vs-2022\">install Visual Studio<\/a> with the Node.js development workload. This will include the <a href=\"https:\/\/learn.microsoft.com\/en-us\/visualstudio\/javascript\/javascript-in-visual-studio?view=vs-2022\">JavaScript\/TypeScript (JSTS) projects<\/a> and the associated support. The code for this app can be found at <a href=\"https:\/\/github.com\/sayedihashimi\/todojswebapp\">sayedihashimi\/todojswebapp (github.com)<\/a>. Create a new React project using the New Project Dialog in Visual Studio 2022. Use <em>File &gt; New &gt; Project \u2026<\/em> to open that dialog. For this tutorial select the React App JavaScript template which is shown below.<\/p>\n<p><img decoding=\"async\" class=\"alignnone wp-image-250719\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/a-screenshot-of-a-computer-program-description-au.png\" alt=\"Image of React App in Visual Studio start screen\" width=\"1014\" height=\"727\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/a-screenshot-of-a-computer-program-description-au.png 1014w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/a-screenshot-of-a-computer-program-description-au-300x215.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/a-screenshot-of-a-computer-program-description-au-768x551.png 768w\" sizes=\"(max-width: 1014px) 100vw, 1014px\" \/><\/p>\n<p>After clicking Next, give the project the name <em>TodoWebApp<\/em> and click Create. This will create the JavaScript project using the <a href=\"https:\/\/vitejs.dev\/\">vite command line tool<\/a>. Now create a new component to encapsulate the todo list functionality. First create a new folder named <code>components<\/code> under the <code>src<\/code> folder. This is the folder where all components will go. It\u2019s a common convention to place components in a <em>components<\/em> folder, but this is not required. Right click on this new folder and select <em>Add &gt; React JSX Component File<\/em>, give it the name <em>TodoList<\/em> as shown in the following image.<\/p>\n<p><img decoding=\"async\" class=\"alignnone wp-image-250720\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-2.png\" alt=\"Image selecting the React JSX Component file\" width=\"941\" height=\"653\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-2.png 941w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-2-300x208.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-2-768x533.png 768w\" sizes=\"(max-width: 941px) 100vw, 941px\" \/><\/p>\n<p>This will create a new jsx file in the components folder. For now, replace the Hello World content with the h2 as shown in the snippet below.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">function TodoList() {\r\n    return (\r\n        &lt;h2&gt;TODO app contents&lt;\/h2&gt;\r\n    );\r\n}<\/code><\/pre>\n<p>This component displays a header for now. We will replace this soon. Let\u2019s wire up this component in the app and then later replace the contents with the desired functionality. Let\u2019s add this component to the app and remove the boilerplate content that is no longer needed.<\/p>\n<p>Open the App.jsx file. This is the main component that is loaded which represents the todo application. This is used in the main.jsx file. In App.jsx, remove all the imports from the top and clear out the contents of the return statement. The file should look like the snippet below.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">function App() {\r\n    return (\r\n\r\n    )\r\n}\r\n\r\nexport default App<\/code><\/pre>\n<p>To add the TodoList component, place your cursor inside the fragment and then type &lt;TodoL RETURN. This will add the component and the import statement. You can see this in the below animation.<\/p>\n<p><img decoding=\"async\" class=\"alignnone wp-image-250721\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-3.gif\" alt=\"Gif showing the return TodoList in function App()\" width=\"1920\" height=\"1080\" \/><\/p>\n<p>Now let\u2019s clear out the CSS files so that we can start with a fresh start. Open the App.css file and delete all the contents. In the <em>Index.css<\/em> file remove all contents except the styles for :root. The <em>Index.css<\/em> file should look like the following.<\/p>\n<pre class=\"prettyprint language-css\"><code class=\"language-css\">:root {\r\n    font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;\r\n    line-height: 1.5;\r\n    font-weight: 400;\r\n    color-scheme: light dark;\r\n    color: rgba(255, 255, 255, 0.87);\r\n    background-color: #242424;\r\n}<\/code><\/pre>\n<p>Now run the app to ensure that everything is working as expected. You can use the Start Debugging button from the toolbar or the F5 keyboard shortcut. After that a browser window should open with the following content.<\/p>\n<p><img decoding=\"async\" class=\"alignnone wp-image-250722\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/a-screenshot-of-a-computer-description-automatica-4.png\" alt=\"Image showing localhost todo list\" width=\"835\" height=\"463\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/a-screenshot-of-a-computer-description-automatica-4.png 835w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/a-screenshot-of-a-computer-description-automatica-4-300x166.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/a-screenshot-of-a-computer-description-automatica-4-768x426.png 768w\" sizes=\"(max-width: 835px) 100vw, 835px\" \/><\/p>\n<p>This would be a good time to create a commit in case you need to roll back to a good state later. Now that you\u2019ve started the app you can leave it running, as you make changes the app will automatically refresh with the latest content using the Hot Module Replacement support. Some actions like adding folders or renaming files will require that you stop debugging and then restart it but in general you can leave it running in the background as you develop your app. Open the TodoList.jsx component so that we can start to define that.<\/p>\n<p>In TodoList.jsx first add the UI needed to show and manage todo entries and then hook it up to store the values after that. Update TodoList.jsx to have the following content.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">function TodoList() {\r\n    return (\r\n        &lt;div&gt;\r\n            &lt;h1&gt;TODO&lt;\/h1&gt;\r\n            &lt;div&gt;\r\n                &lt;input\r\n                    type=\"text\"\r\n                    placeholder=\"Enter a task\"\r\n                    required\r\n                    aria-label=\"Task text\" \/&gt;\r\n                &lt;button className=\"add-button\" aria-label=\"Add task\" &gt;\r\n                    Add\r\n                &lt;\/button&gt;\r\n            &lt;\/div&gt;\r\n            &lt;ol id=\"todo-list\"&gt;\r\n                &lt;p&gt;existing tasks will be shown here&lt;\/p&gt;\r\n            &lt;\/ol&gt;\r\n        &lt;\/div&gt;\r\n    );\r\n}\r\n\r\nexport default TodoList;<\/code><\/pre>\n<p>We have added an input box for the todo item as well as a button to submit that. The existing tasks will be displayed under the input and button, but we will get to that soon. Now that we have the input field to get a new task added, we will wire up the Add button. We will use the <a href=\"https:\/\/react.dev\/reference\/react\/useState\">useState<\/a> React hook to add two state variables, one for the task which is getting added and another to store the existing tasks. For this tutorial the tasks will be stored in memory we will not add a database for persistent storage. Add the following import statement to TodoList.jsx to import useState.<\/p>\n<p><code>import { useState } from 'react'<\/code><\/p>\n<p>Now we can use that hook to create the state variables. Add the following code in the TodoList function above the return statement.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">const [tasks, setTasks] = useState([\r\n    \"Drink some coffee\",\r\n    \"Create a TODO app\",\r\n    \"Drink some more coffee\"]);\r\n\r\nconst [newTaskText, setNewTaskText] = useState(\"\");<\/code><\/pre>\n<p>This will setup two variables, <code>tasks<\/code> and <code>newTaskText<\/code>, for the data and two functions that you can call to update those variables, <code>setTasks<\/code> and <code>setNewTasks<\/code>. When a value for a state variable is changed React will automatically re-render the component. We are almost ready to update TodoList.jsx to show the todo items as a list, but there is an important React concept that we must explain before doing so.<\/p>\n<p>In React when you display a list of items you will need to add a key to uniquely identify each item in the list. This is explained in depth in the React docs at <a href=\"https:\/\/react.dev\/learn\/rendering-lists\">Rendering Lists<\/a>, but we will cover what is needed for this tutorial. We have a list of todo items that we want to display, and we need to associate a unique key for each item, the key for each item should not change. Since the key for each item should not change, we cannot use the index of the item in the array as the key. We need an id which will not change through out the lifetime of those values. We will use <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Crypto\/randomUUID\">randomUUID()<\/a> to create a unique id for each todo item.<\/p>\n<p>Now let\u2019s create TodoList.jsx using a uuid as the key for each todo item. Update TodoList.jsx to contain the following code.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">import React, { useState } from 'react';\r\n\r\nconst initialTasks = [\r\n    { id: self.crypto.randomUUID(), text: 'Drink some coffee' },\r\n    { id: self.crypto.randomUUID(), text: 'Create a TODO app' },\r\n    { id: self.crypto.randomUUID(), text: 'Drink some more coffee' }\r\n];\r\n\r\nfunction TodoList() {\r\n    const [tasks, setTasks] = useState(initialTasks);\r\n    const [newTaskText, setNewTaskText] = useState(\"\");\r\n\r\n    return (\r\n        &lt;article\r\n            className=\"todo-list\"\r\n            aria-label=\"task list manager\"&gt;\r\n            &lt;header&gt;\r\n                &lt;h1&gt;TODO&lt;\/h1&gt;\r\n                &lt;form className=\"todo-input\" aria-controls=\"todo-list\"&gt;\r\n                    &lt;input\r\n                        type=\"text\"\r\n                        placeholder=\"Enter a task\"\r\n                        value={newTaskText} \/&gt;\r\n                    &lt;button\r\n                        className=\"add-button\"&gt;\r\n                        Add\r\n                    &lt;\/button&gt;\r\n                &lt;\/form&gt;\r\n            &lt;\/header&gt;\r\n            &lt;ol id=\"todo-list\" aria-live=\"polite\" aria-label=\"task list\"&gt;\r\n                {tasks.map((task, index) =&gt;\r\n                    &lt;li key={task.id}&gt;\r\n                        &lt;span className=\"text\"&gt;{task.text}&lt;\/span&gt;\r\n                    &lt;\/li&gt;\r\n                )}\r\n            &lt;\/ol&gt;\r\n        &lt;\/article&gt;\r\n    );\r\n}\r\nexport default TodoList;<\/code><\/pre>\n<p>Since the id values are assigned outside the TodoList function, we can be sure the values will not change if the page is rerendered. When you try the app in this state, you will notice that you cannot type into the todo input element. This is because the input element is bound to <code>newTaskText<\/code> which has been initialized to a blank string. To allow users to add new tasks, we will need to handle the <code>onChange<\/code> event on that control. We also need to implement the Add button support. Add the functions immediately above the return statement in the TodoList function.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">function handleInputChange(event) {\r\n    setNewTaskText(event.target.value);\r\n}\r\n\r\nfunction addTask() {\r\n    if (newTaskText.trim() !== \"\") {\r\n        setTasks(t =&gt; [...t, { id: self.crypto.randomUUID(), text: newTaskText }]);\r\n        setNewTaskText(\"\");\r\n    }\r\n    event.preventDefault();\r\n}<\/code><\/pre>\n<p>In the <code>handleInputChanged<\/code> function, the new value from the input field is passed in via <code>event.target.value<\/code> and that is used to update the value for the <code>newTaskText<\/code> variable with <code>setNewTaskText<\/code>. In the <code>addTask<\/code> function we add the new task to the list of existing tasks using <code>setTasks<\/code> and we set the id of the item as a new uuid value. Update the input element to include <code>onChange={handleInputChange}<\/code> and update the Add button to include <code>onClick={addTask}<\/code>. This will wire up the event to the function that handles that event. Following this you should be able to add a new task to the task list. New tasks are added to the bottom of the list. To make this app more useful, we need to add support to delete tasks and to move a task up or down. Let\u2019s get started on that.<\/p>\n<p>We will add the functions to support delete, move up and move down and then update the markup to show a button for each action. Add the following in the TodoList function above the return statement.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">function deleteTask(id) {\r\n    const updatedTasks = tasks.filter(task =&gt; task.id != id);\r\n    setTasks(updatedTasks);\r\n}\r\n\r\nfunction moveTaskUp(index) {\r\n    if (index &gt; 0) {\r\n        const updatedTasks = [...tasks];\r\n        [updatedTasks[index], updatedTasks[index - 1]] = [updatedTasks[index - 1], updatedTasks[index]];\r\n        setTasks(updatedTasks);\r\n    }\r\n}\r\n\r\nfunction moveTaskDown(index) {\r\n    if (index &lt; tasks.length) {\r\n        const updatedTasks = [...tasks];\r\n        [updatedTasks[index], updatedTasks[index + 1]] = [updatedTasks[index + 1], updatedTasks[index]];\r\n        setTasks(updatedTasks);\r\n    }\r\n}<\/code><\/pre>\n<p>The delete function takes in the task id and deletes that one from the list and uses the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Array\/filter\">Array <code>filter()<\/code><\/a> method to create a new array excluding the selected item and then calls <code>setTasks()<\/code>. The other two functions take in the index of the item since this is specific to the item ordering. Both <code>moveTaskUp()<\/code> and <code>moveTaskDown()<\/code> use <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/Destructuring_assignment\">array destructuring assignment<\/a> to swap the selected task with its neighbor. Now we will update the view to include these buttons. Update the return statement to contain the following.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">return (\r\n    &lt;article\r\n        className=\"todo-list\"\r\n        aria-label=\"task list manager\"&gt;\r\n        &lt;header&gt;\r\n            &lt;h1&gt;TODO&lt;\/h1&gt;\r\n            &lt;form className=\"todo-input\" onSubmit={addTask} aria-controls=\"todo-list\"&gt;\r\n                &lt;input\r\n                    type=\"text\"\r\n                    required\r\n                    autoFocus\r\n                    placeholder=\"Enter a task\"\r\n                    value={newTaskText}\r\n                    aria-label=\"Task text\"\r\n                    onChange={handleInputChange} \/&gt;\r\n                &lt;button\r\n                    className=\"add-button\"\r\n                    aria-label=\"Add task\"&gt;\r\n                    Add\r\n                &lt;\/button&gt;\r\n            &lt;\/form&gt;\r\n        &lt;\/header&gt;\r\n        &lt;ol id=\"todo-list\" aria-live=\"polite\"&gt;\r\n            {tasks.map((task, index) =&gt;\r\n                &lt;li key={task.id}&gt;\r\n                    &lt;span className=\"text\"&gt;{task.text}&lt;\/span&gt;\r\n                    &lt;button className=\"delete-button\" onClick={() =&gt; deleteTask(task.id)}&gt;\r\n                        \ud83d\uddd1\ufe0f\r\n                    &lt;\/button&gt;\r\n                    &lt;button className=\"up-button\" onClick={() =&gt; moveTaskUp(index)}&gt;\r\n                        \u21e7\r\n                    &lt;\/button&gt;\r\n                    &lt;button className=\"down-button\" onClick={() =&gt; moveTaskDown(index)}&gt;\r\n                        \u21e9\r\n                    &lt;\/button&gt;\r\n                &lt;\/li&gt;\r\n            )}\r\n        &lt;\/ol&gt;\r\n    &lt;\/article&gt;\r\n);<\/code><\/pre>\n<p>We have added the buttons needed to perform the tasks we discussed above. We are using Unicode characters as icons on the buttons. In the markup there are some attributes added for when we add some CSS later. You will also notice the use of <a href=\"https:\/\/www.w3.org\/WAI\/standards-guidelines\/aria\/\">aria attributes<\/a> to improve accessibility, the are optional but highly recommended. If you run the app it should look like the image below.<\/p>\n<p><img decoding=\"async\" class=\"alignnone wp-image-250723\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-5.png\" alt=\"Image showing the UI for the todo list with the garbage icon and the up\/down arrows\" width=\"478\" height=\"320\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-5.png 478w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-5-300x201.png 300w\" sizes=\"(max-width: 478px) 100vw, 478px\" \/><\/p>\n<p>You should now be able to perform the following in the TODO web app.<\/p>\n<ul>\n<li>Add task<\/li>\n<li>Delete task<\/li>\n<li>Move task up<\/li>\n<li>Move task down<\/li>\n<\/ul>\n<p>This is working well, but we can refactor this so that we have a reusable component to display the todo items. The markup for the todo item will go into a new component, TodoItem. Since the management of the list will stay in the Todo component, we will pass callbacks to the delete and move buttons. To get started, right click the components folder in the Solution Explorer and select Add &gt; New Item. In the dialog that opens select the React JSX Component File. Give it the name TodoItem and click Add. We will pass in the task and callbacks as props to this new component. The TodoItem should contain the following code.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">import PropTypes from 'prop-types';\r\n\r\nfunction TodoItem({ task, deleteTaskCallback, moveTaskUpCallback, moveTaskDownCallback }) {\r\n    return (\r\n        &lt;li aria-label=\"task\" &gt;\r\n            &lt;span className=\"text\"&gt;{task}&lt;\/span&gt;\r\n            &lt;button\r\n                type=\"button\"\r\n                aria-label=\"Delete task\"\r\n                className=\"delete-button\"\r\n                onClick={() =&gt; deleteTaskCallback()}&gt;\r\n                \ud83d\uddd1\ufe0f\r\n            &lt;\/button&gt;\r\n            &lt;button\r\n                type=\"button\"\r\n                aria-label=\"Move task up\"\r\n                className=\"up-button\"\r\n                onClick={() =&gt; moveTaskUpCallback()}&gt;\r\n                \u21e7\r\n            &lt;\/button&gt;\r\n            &lt;button\r\n                type=\"button\"\r\n                aria-label=\"Move task down\"\r\n                className=\"down-button\"\r\n                onClick={() =&gt; moveTaskDownCallback()}&gt;\r\n                \u21e9\r\n            &lt;\/button&gt;\r\n        &lt;\/li&gt;\r\n    );\r\n}\r\n\r\nTodoItem.propTypes = {\r\n    task: PropTypes.string.isRequired,\r\n    deleteTaskCallback: PropTypes.func.isRequired,\r\n    moveTaskUpCallback: PropTypes.func.isRequired,\r\n    moveTaskDownCallback: PropTypes.func.isRequired,\r\n};\r\n\r\nexport default TodoItem;<\/code><\/pre>\n<p>The code above contains the markup from the Todo component and at the bottom we declare the propTypes. Props are used to pass data from a parent component to child components. For more info on props the docs are available at <a href=\"https:\/\/react.dev\/learn\/passing-props-to-a-component\">Passing Props to a Component \u2013 React<\/a>. Since the delete and move functions are being passed in as callbacks, the onClick handler will need to be updated to call into those callbacks. The full code for TodoList is below that uses the TodoItem component.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">import React, { useState } from 'react'\r\nimport TodoItem from '.\/TodoItem'\r\n\r\nconst initialTasks = [\r\n    { id: self.crypto.randomUUID(), text: 'Drink some coffee' },\r\n    { id: self.crypto.randomUUID(), text: 'Create a TODO app' },\r\n    { id: self.crypto.randomUUID(), text: 'Drink some more coffee' }\r\n];\r\n\r\nfunction TodoList() {\r\n    const [tasks, setTasks] = useState(initialTasks);\r\n    const [newTaskText, setNewTaskText] = useState(\"\");\r\n    function handleInputChange(event) {\r\n        setNewTaskText(event.target.value);\r\n    }\r\n    function addTask() {\r\n        if (newTaskText.trim() !== \"\") {\r\n            setTasks(t =&gt; [...t, { id: self.crypto.randomUUID(), text: newTaskText }]);\r\n            setNewTaskText(\"\");\r\n        }\r\n        event.preventDefault();\r\n    }\r\n    function deleteTask(id) {\r\n        const updatedTasks = tasks.filter(task =&gt; task.id !== id);\r\n        setTasks(updatedTasks);\r\n    }\r\n    function moveTaskUp(index) {\r\n        if (index &gt; 0) {\r\n            const updatedTasks = [...tasks];\r\n            [updatedTasks[index], updatedTasks[index - 1]] = [updatedTasks[index - 1], updatedTasks[index]];\r\n            setTasks(updatedTasks);\r\n        }\r\n    }\r\n    function moveTaskDown(index) {\r\n        if (index &lt; tasks.length) {\r\n            const updatedTasks = [...tasks];\r\n            [updatedTasks[index], updatedTasks[index + 1]] = [updatedTasks[index + 1], updatedTasks[index]];\r\n            setTasks(updatedTasks);\r\n        }\r\n    }\r\n    return (\r\n        &lt;article\r\n            className=\"todo-list\"\r\n            aria-label=\"task list manager\"&gt;\r\n            &lt;header&gt;\r\n                &lt;h1&gt;TODO&lt;\/h1&gt;\r\n                &lt;form onSubmit={addTask} aria-controls=\"todo-list\"&gt;\r\n                    &lt;input\r\n                        type=\"text\"\r\n                        required\r\n                        placeholder=\"Enter a task\"\r\n                        value={newTaskText}\r\n                        aria-label=\"Task text\"\r\n                        onChange={handleInputChange} \/&gt;\r\n                    &lt;button\r\n                        className=\"add-button\"\r\n                        aria-label=\"Add task\"&gt;\r\n                        Add\r\n                    &lt;\/button&gt;\r\n                &lt;\/form&gt;\r\n            &lt;\/header&gt;\r\n            &lt;ol id=\"todo-list\" aria-live=\"polite\"&gt;\r\n                {tasks.map((task, index) =&gt;\r\n                    &lt;TodoItem\r\n                        key={task.id}\r\n                        task={task.text}\r\n                        deleteTaskCallback={() =&gt; deleteTask(task.id)}\r\n                        moveTaskUpCallback={() =&gt; moveTaskUp(index)}\r\n                        moveTaskDownCallback={() =&gt; moveTaskDown(index)}\r\n                    \/&gt;\r\n                )}\r\n            &lt;\/ol&gt;\r\n        &lt;\/article&gt;\r\n    );\r\n}\r\n\r\nexport default TodoList;<\/code><\/pre>\n<p>Now the TodoItem component is used to render each todo item. Notice that we set the key to <code>task.id<\/code> which contains the uuid value for that task. When you run the app, you shouldn\u2019t see any changes to the look or behavior of the app since we refactored it to use TodoItem. Let\u2019s move on to styling.<\/p>\n<p>Now that we have all the basic functions supported, it\u2019s time to start adding some styling to this to make it look nice. Now would be a good time to create a commit to save your progress. Let\u2019s first start by adding a link in the Index.html for a font family, Inter, that we will use for this app. In the Index.html there are some other items that need to be cleaned up. Specifically, the title should be updated and vite.svg file is still being used as the icon. Update the Index.html file to have the following content.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">&lt;!doctype html&gt;\r\n&lt;html lang=\"en\"&gt;\r\n    &lt;head&gt;\r\n        &lt;meta charset=\"UTF-8\" \/&gt;\r\n        &lt;link rel=\"icon\" type=\"image\/svg+xml\" href=\"\/checkmark-square.svg\" \/&gt;\r\n        &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" \/&gt;\r\n        &lt;title&gt;TODO app&lt;\/title&gt;\r\n        &lt;link href='https:\/\/fonts.googleapis.com\/css?family=Inter' rel='stylesheet'&gt;\r\n        &lt;script type=\"module\" defer src=\"\/src\/main.jsx\"&gt;&lt;\/script&gt;\r\n    &lt;\/head&gt;\r\n    &lt;body&gt;\r\n    &lt;\/body&gt;\r\n&lt;\/html&gt;<\/code><\/pre>\n<p>Edit the main.jsx file to replace &#8216;root&#8217; with &#8216;main&#8217; when calling createRoot the full content for that file is below.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">import { StrictMode } from 'react'\r\nimport { createRoot } from 'react-dom\/client'\r\nimport App from '.\/App.jsx'\r\nimport '.\/index.css'\r\n\r\ncreateRoot(document.querySelector('main')).render(\r\n    &lt;StrictMode&gt;\r\n        &lt;App \/&gt;\r\n    &lt;\/StrictMode&gt;,\r\n)<\/code><\/pre>\n<p>In addition to these changes the file checkmark-square.svg was added to the public folder. This is an SVG from the FluentUI checkmark square image which I downloaded directly. There is a package that you can use for a more integrated experience but that\u2019s outside the scope of this post.<\/p>\n<p>Now let\u2019s update the styles of the TodoList component. In the components folder add a new CSS file named TodoList.css. You can right click on the project and select Add &gt; New Item and then select Style Sheet. Give the file the name TodoList.css. Add the following to TodoList.css.<\/p>\n<pre class=\"prettyprint language-css\"><code class=\"language-css\">.todo-list {\r\n    background-color: #1e1e1e;\r\n    padding: 1.25rem;\r\n    border-radius: 0.5rem;\r\n    box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.3);\r\n    width: 100%;\r\n    max-width: 25rem;\r\n}\r\n\r\n.todo-list h1 {\r\n    text-align: center;\r\n    color: #e0e0e0;\r\n}\r\n\r\n.todo-input {\r\n    display: flex;\r\n    justify-content: space-between;\r\n    margin-bottom: 1.25rem;\r\n}\r\n\r\n.todo-input input {\r\n    flex: 1;\r\n    padding: 0.625rem;\r\n    border: 0.0625rem solid #333;\r\n    border-radius: 0.25rem;\r\n    margin-right: 0.625rem;\r\n    background-color: #2c2c2c;\r\n    color: #e0e0e0;\r\n}\r\n\r\n.todo-input .add-button {\r\n    padding: 0.625rem 1.25rem;\r\n    background-color: #007bff;\r\n    color: #fff;\r\n    border: none;\r\n    border-radius: 0.25rem;\r\n    cursor: pointer;\r\n}\r\n\r\n.todo-input .add-button:hover {\r\n    background-color: #0056b3;\r\n}\r\n\r\n.todo-list ol {\r\n    list-style-type: none;\r\n    padding: 0;\r\n}\r\n\r\n.todo-list li {\r\n    display: flex;\r\n    justify-content: space-between;\r\n    align-items: center;\r\n    padding: 0.625rem;\r\n    border-bottom: 0.0625rem solid #333;\r\n}\r\n\r\n.todo-list li:last-child {\r\n    border-bottom: none;\r\n}\r\n\r\n.todo-list .text {\r\n    flex: 1;\r\n}\r\n\r\n.todo-list li button {\r\n    background: none;\r\n    border: none;\r\n    cursor: pointer;\r\n    font-size: 1rem;\r\n    margin-left: 0.625rem;\r\n    color: #e0e0e0;\r\n}\r\n\r\n.todo-list li button:hover {\r\n    color: #007bff;\r\n}\r\n\r\n.todo-list li button.delete-button {\r\n    color: #ff4d4d;\r\n}\r\n\r\n.todo-list li button.up-button,\r\n.todo-list li button.down-button {\r\n    color: #4caf50;\r\n}<\/code><\/pre>\n<p>After that edit TodoList.jsx to add the following import at the top of the file.<\/p>\n<pre class=\"prettyprint language-js\"><code class=\"language-js\">import '.\/TodoList.css';<\/code><\/pre>\n<p>Refresh the browser after saving the changes. This should improve the styling of the app. The app should look like the following screenshot.<\/p>\n<p><img decoding=\"async\" class=\"alignnone wp-image-250724\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-6.png\" alt=\"Image showing the final todo list cleaned up view\" width=\"1250\" height=\"816\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-6.png 1250w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-6-300x196.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-6-1024x668.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/09\/word-image-250718-6-768x501.png 768w\" sizes=\"(max-width: 1250px) 100vw, 1250px\" \/><\/p>\n<p>Now you have built a working Todo app which stores the todo items in memory. From here you could update the app to store the todo items in <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Window\/localStorage\">localStorage<\/a>\/<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/IndexedDB_API\">IndexedDb<\/a>, or integrate this with a server-side database, or other backend, for more permanent storage.<\/p>\n<h2>Recap<\/h2>\n<p>In this tutorial you have created a new React app using Visual Studio 2022. The app consists of a todo list, which includes support to add tasks, delete tasks and to reorder them. You created two new React components and used those throughout this tutorial.<\/p>\n<h2>Resources<\/h2>\n<p>Below are some links to related resources.<\/p>\n<ul>\n<li>Code for this sample <a href=\"https:\/\/github.com\/sayedihashimi\/todojswebapp\">sayedihashimi\/todojswebapp (github.com)<\/a><\/li>\n<li><a href=\"https:\/\/learn.microsoft.com\/en-us\/visualstudio\/javascript\/?view=vs-2022\">Visual Studio JavaScript and TypeScript projects<\/a><\/li>\n<\/ul>\n<h2>Feedback<\/h2>\n<p>You can share feedback with us via\u00a0<a href=\"https:\/\/developercommunity.visualstudio.com\/home\">Developer Community<\/a>: report any bugs or issues via\u00a0<a href=\"https:\/\/docs.microsoft.com\/visualstudio\/ide\/how-to-report-a-problem-with-visual-studio\">report a problem<\/a>\u00a0and share your\u00a0<a href=\"https:\/\/developercommunity.visualstudio.com\/report?space=8&amp;entry=suggestion\">suggestions for new features<\/a>\u00a0or improvements to existing ones. You can also leave a comment here or reach out to Sayed on Twitter at\u00a0<a href=\"https:\/\/twitter.com\/sayedihashimi\">@SayedIHashimi<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial we will create a React front-end, with JavaScript, of a TODO web app using a Visual Studio 2022. To get started install Visual Studio with the Node.js development workload. This will include the JavaScript\/TypeScript (JSTS) projects and the associated support. The code for this app can be found at sayedihashimi\/todojswebapp (github.com). Create [&hellip;]<\/p>\n","protected":false},"author":357,"featured_media":250724,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[6963,155],"tags":[],"class_list":["post-250718","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-productivity","category-visual-studio"],"acf":[],"blog_post_summary":"<p>In this tutorial we will create a React front-end, with JavaScript, of a TODO web app using a Visual Studio 2022. To get started install Visual Studio with the Node.js development workload. This will include the JavaScript\/TypeScript (JSTS) projects and the associated support. The code for this app can be found at sayedihashimi\/todojswebapp (github.com). Create [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/250718","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/users\/357"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/comments?post=250718"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/250718\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media\/250724"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media?parent=250718"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/categories?post=250718"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/tags?post=250718"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}