# notes

path: Omnivore/2023-11-21 - The Life-Changing Magic of Tidying Up Your To-Do List.md
sortable_path: Omnivore2023-11-21 - The Life-Changing Magic of Tidying Up Your To-Do List.md
title: The Life-Changing Magic of Tidying Up Your To-Do List
lead: # The Life-Changing Magic of Tidying Up Your To-Do List
checksum: c11e6a124b926f2ea0d72bb3931f762f12bfa22d369708d7bc12f7bdfe1c4e4f
created: 2024-09-15 00:40:57.626916939+00:00
modified: 2024-09-22 22:40:13.39451104+00:00
body:
  # The Life-Changing Magic of Tidying Up Your To-Do List

  ## Notes

  Before you even start looking at your tasks, write down what having a neatly organized and prioritized to-do list would mean for your life. Maybe you want to run a successful business, get in shape, be more present with your family, have closer relationships with friends, or lead a more adventurous life.

  Find a medium that lets you truly envision the details. You can describe it in words, mind map it, draw it out, create a Pinterest board, collect YouTube videos, or brainstorm in whatever form suits you.

  Why do you want to get in shape? The answer might be "to have more energy and feel more confident."  Why do you want to have more energy and feel more confident? Maybe the answer is "to be more fully yourself and stop worrying about what other people think of you." Ask yourself "why" 3-5 times for every item in your vision.
  ## Original

  ---

  ## Other readers also enjoyed...

  ![](https://proxy-prod.omnivore-image-cache.app/340x180,sZ_977ycD6VGwVRgscq88kMFQTyjSqu6XZhquhlHBmEM/https://res.cloudinary.com/imagist/image/fetch/q_auto/f_auto/c_scale,w_2624/https%3A%2F%2Ftdinspiration.wpengine.com%2Fwp-content%2Fuploads%2F2015%2F12%2FBanner_image%402x1.png)

  ## How to Vanquish Busywork and Spend More Time on What Matters

  These tips for improving your focus and productivity will help you avoid getting sucked into busywork.

  [Read more](https://todoist.com/inspiration/busywork-productivity-focus)
  * [Productivity](https://todoist.com/inspiration/category/productivity)

  ![](https://proxy-prod.omnivore-image-cache.app/340x180,sYXX0C2yKXuKVXm2tjpJ8fNVgmGJUVGqDXIl5u7JvUvE/https://res.cloudinary.com/imagist/image/fetch/q_auto/f_auto/c_scale,w_2624/https%3A%2F%2Ftdinspiration.wpengine.com%2Fwp-content%2Fuploads%2F2019%2F03%2FNegotiate_Working_from_Home-scaled.jpg)

  ## How to Ask to Work From Home (With Exact Scripts & Email Templates to Aid Your Negotiation)

  Follow these 9 steps to secure a remote work arrangement even in the most skeptical of organizations

  [Read more](https://todoist.com/inspiration/how-to-ask-to-work-from-home)
  * [Remote Work](https://todoist.com/inspiration/category/remote-work)

  ![](https://proxy-prod.omnivore-image-cache.app/340x180,snqFtOXYibl3YNEzgIr_nXxyF6xQm3nbVTPgILKPaK_8/https://res.cloudinary.com/imagist/image/fetch/q_auto/f_auto/c_scale,w_2624/https%3A%2F%2Ftdinspiration.wpengine.com%2Fwp-content%2Fuploads%2F2020%2F12%2F1.-Banner_Doist_Angel-scaled.jpg)

  ## How to Complete Your Own Annual Review

  Reflect on work, health, finances, and more with a personal year in review

  [Read more](https://todoist.com/inspiration/annual-review)
  * [Goals](https://todoist.com/inspiration/category/goals)

raw_content:
  ---
  id: f5956559-3eb2-4717-93bd-3f3a2f0c5549
  title: |
    The Life-Changing Magic of Tidying Up Your To-Do List
  status: ARCHIVED
  tags:
    - read-later
  date_added: 2023-11-21 08:05:01
  url_omnivore: |
    https://omnivore.app/me/https-todoist-com-inspiration-life-changing-magic-tidying-todois-18bf18e00a2
  url_original: |
    https://todoist.com/inspiration/life-changing-magic-tidying-todoist
  ---

  # The Life-Changing Magic of Tidying Up Your To-Do List

  ## Notes

  Before you even start looking at your tasks, write down what having a neatly organized and prioritized to-do list would mean for your life. Maybe you want to run a successful business, get in shape, be more present with your family, have closer relationships with friends, or lead a more adventurous life.

  Find a medium that lets you truly envision the details. You can describe it in words, mind map it, draw it out, create a Pinterest board, collect YouTube videos, or brainstorm in whatever form suits you.

  Why do you want to get in shape? The answer might be "to have more energy and feel more confident."  Why do you want to have more energy and feel more confident? Maybe the answer is "to be more fully yourself and stop worrying about what other people think of you." Ask yourself "why" 3-5 times for every item in your vision.
  ## Original

  ---

  ## Other readers also enjoyed...

  ![](https://proxy-prod.omnivore-image-cache.app/340x180,sZ_977ycD6VGwVRgscq88kMFQTyjSqu6XZhquhlHBmEM/https://res.cloudinary.com/imagist/image/fetch/q_auto/f_auto/c_scale,w_2624/https%3A%2F%2Ftdinspiration.wpengine.com%2Fwp-content%2Fuploads%2F2015%2F12%2FBanner_image%402x1.png)

  ## How to Vanquish Busywork and Spend More Time on What Matters

  These tips for improving your focus and productivity will help you avoid getting sucked into busywork.

  [Read more](https://todoist.com/inspiration/busywork-productivity-focus)
  * [Productivity](https://todoist.com/inspiration/category/productivity)

  ![](https://proxy-prod.omnivore-image-cache.app/340x180,sYXX0C2yKXuKVXm2tjpJ8fNVgmGJUVGqDXIl5u7JvUvE/https://res.cloudinary.com/imagist/image/fetch/q_auto/f_auto/c_scale,w_2624/https%3A%2F%2Ftdinspiration.wpengine.com%2Fwp-content%2Fuploads%2F2019%2F03%2FNegotiate_Working_from_Home-scaled.jpg)

  ## How to Ask to Work From Home (With Exact Scripts & Email Templates to Aid Your Negotiation)

  Follow these 9 steps to secure a remote work arrangement even in the most skeptical of organizations

  [Read more](https://todoist.com/inspiration/how-to-ask-to-work-from-home)
  * [Remote Work](https://todoist.com/inspiration/category/remote-work)

  ![](https://proxy-prod.omnivore-image-cache.app/340x180,snqFtOXYibl3YNEzgIr_nXxyF6xQm3nbVTPgILKPaK_8/https://res.cloudinary.com/imagist/image/fetch/q_auto/f_auto/c_scale,w_2624/https%3A%2F%2Ftdinspiration.wpengine.com%2Fwp-content%2Fuploads%2F2020%2F12%2F1.-Banner_Doist_Angel-scaled.jpg)

  ## How to Complete Your Own Annual Review

  Reflect on work, health, finances, and more with a personal year in review

  [Read more](https://todoist.com/inspiration/annual-review)
  * [Goals](https://todoist.com/inspiration/category/goals)

metadata:
  {"date_added":"2023-11-21 08:05:01","id":"f5956559-3eb2-4717-93bd-3f3a2f0c5549","status":"ARCHIVED","tags":["read-later"],"title":"The Life-Changing Magic of Tidying Up Your To-Do List\n","url_omnivore":"https://omnivore.app/me/https-todoist-com-inspiration-life-changing-magic-tidying-todois-18bf18e00a2\n","url_original":"https://todoist.com/inspiration/life-changing-magic-tidying-todoist\n"}


path: Omnivore/2023-11-21 - tbaggery - A Note About Git Commit Messages.md
sortable_path: Omnivore2023-11-21 - tbaggery - A Note About Git Commit Messages.md
title: tbaggery - A Note About Git Commit Messages
lead: # tbaggery - A Note About Git Commit Messages
checksum: 64cc7a0b238e3c8e3c9b41ec7cab28300a0fb721dacdc4fc0db9669af4a521dc
created: 2024-09-15 00:40:57.660445031+00:00
modified: 2024-09-22 22:40:13.39451104+00:00
body:
  # tbaggery - A Note About Git Commit Messages

  ## Notes

  - Tratar de mantener el limite que muestra Treesitter
  - Utilizar la primera linea como "el asunto de un correo"
  - Escribir el cuerpo con una separación de una linea en blanco
  - Usar lenguaje imperativo, _"Fix bug"_ en vez de _"Fixes bug."_.
  ## Original

  I want to take a moment to elaborate on what makes a well formed commit message. I think the best practices for commit message formatting is one of the little details that makes Git great. Understandably, some of the first commits to rails.git have messages of the really-long-line variety, and I want to expand on why this is a poor practice.

  Here’s a model Git commit message:

  ```livecodeserver
  Capitalized, short (50 chars or less) summary

  More detailed explanatory text, if necessary.  Wrap it to about 72
  characters or so.  In some contexts, the first line is treated as the
  subject of an email and the rest of the text as the body.  The blank
  line separating the summary from the body is critical (unless you omit
  the body entirely); tools like rebase can get confused if you run the
  two together.

  Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
  or "Fixes bug."  This convention matches up with commit messages generated
  by commands like git merge and git revert.

  Further paragraphs come after blank lines.

  - Bullet points are okay, too

  - Typically a hyphen or asterisk is used for the bullet, followed by a
    single space, with blank lines in between, but conventions vary here

  - Use a hanging indent

  ```

  Let’s start with a few of the reasons why wrapping your commit messages to 72 columns is a good thing.

  * `git log` doesn’t do any special special wrapping of the commit messages. With the default pager of `less -S`, this means your paragraphs flow far off the edge of the screen, making them difficult to read. On an 80 column terminal, if we subtract 4 columns for the indent on the left and 4 more for symmetry on the right, we’re left with 72 columns.
  * `git format-patch --stdout` converts a series of commits to a series of emails, using the messages for the message body. Good email netiquette dictates we wrap our plain text emails such that there’s room for a few levels of nested reply indicators without overflow in an 80 column terminal. (The current rails.git workflow doesn’t include email, but who knows what the future will bring.)

  Vim users can meet this requirement by installing my [vim-git runtime files](http://github.com/tpope/vim-git), or by simply setting the following option in your git commit message file:

  For Textmate, you can adjust the “Wrap Column” option under the view menu, then use `^Q` to rewrap paragraphs (be sure there’s a blank line afterwards to avoid mixing in the comments). Here’s a shell command to add 72 to the menu so you don’t have to drag to select each time:

  ```lsl
  $ defaults write com.macromates.textmate OakWrapColumns '( 40, 72, 78 )'

  ```

  More important than the mechanics of formatting the body is the practice of having a subject line. As the example indicates, you should shoot for about 50 characters (though this isn’t a hard maximum) and always, always follow it with a blank line. This first line should be a concise summary of the changes introduced by the commit; if there are any technical details that cannot be expressed in these strict size constraints, put them in the body instead. The subject line is used all over Git, oftentimes in truncated form if too long of a message was used. The following are just a handful of examples of where it ends up:

  * `git log --pretty=oneline` shows a terse history mapping containing the commit id and the summary
  * `git rebase --interactive` provides the summary for each commit in the editor it invokes
  * if the config option `merge.summary` is set, the summaries from all merged commits will make their way into the merge commit message
  * `git shortlog` uses summary lines in the changelog-like output it produces
  * `git format-patch`, `git send-email`, and related tools use it as the subject for emails
  * reflogs, a local history accessible with `git reflog` intended to help you recover from stupid mistakes, get a copy of the summary
  * `gitk` has a column for the summary
  * GitHub uses the summary in various places in their user interface

  The subject/body distinction may seem unimportant but it’s one of many subtle factors that makes Git history so much more pleasant to work with than Subversion.

raw_content:
  ---
  id: 01b3b6c1-27ae-40a4-99fa-d297febc1e7c
  title: |
    tbaggery - A Note About Git Commit Messages
  status: ARCHIVED
  tags:
    - read-later
  date_added: 2023-11-21 11:59:10
  url_omnivore: |
    https://omnivore.app/me/https-tbaggery-com-2008-04-19-a-note-about-git-commit-messages-h-18bf2646727
  url_original: |
    https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
  ---

  # tbaggery - A Note About Git Commit Messages

  ## Notes

  - Tratar de mantener el limite que muestra Treesitter
  - Utilizar la primera linea como "el asunto de un correo"
  - Escribir el cuerpo con una separación de una linea en blanco
  - Usar lenguaje imperativo, _"Fix bug"_ en vez de _"Fixes bug."_.
  ## Original

  I want to take a moment to elaborate on what makes a well formed commit message. I think the best practices for commit message formatting is one of the little details that makes Git great. Understandably, some of the first commits to rails.git have messages of the really-long-line variety, and I want to expand on why this is a poor practice.

  Here’s a model Git commit message:

  ```livecodeserver
  Capitalized, short (50 chars or less) summary

  More detailed explanatory text, if necessary.  Wrap it to about 72
  characters or so.  In some contexts, the first line is treated as the
  subject of an email and the rest of the text as the body.  The blank
  line separating the summary from the body is critical (unless you omit
  the body entirely); tools like rebase can get confused if you run the
  two together.

  Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
  or "Fixes bug."  This convention matches up with commit messages generated
  by commands like git merge and git revert.

  Further paragraphs come after blank lines.

  - Bullet points are okay, too

  - Typically a hyphen or asterisk is used for the bullet, followed by a
    single space, with blank lines in between, but conventions vary here

  - Use a hanging indent

  ```

  Let’s start with a few of the reasons why wrapping your commit messages to 72 columns is a good thing.

  * `git log` doesn’t do any special special wrapping of the commit messages. With the default pager of `less -S`, this means your paragraphs flow far off the edge of the screen, making them difficult to read. On an 80 column terminal, if we subtract 4 columns for the indent on the left and 4 more for symmetry on the right, we’re left with 72 columns.
  * `git format-patch --stdout` converts a series of commits to a series of emails, using the messages for the message body. Good email netiquette dictates we wrap our plain text emails such that there’s room for a few levels of nested reply indicators without overflow in an 80 column terminal. (The current rails.git workflow doesn’t include email, but who knows what the future will bring.)

  Vim users can meet this requirement by installing my [vim-git runtime files](http://github.com/tpope/vim-git), or by simply setting the following option in your git commit message file:

  For Textmate, you can adjust the “Wrap Column” option under the view menu, then use `^Q` to rewrap paragraphs (be sure there’s a blank line afterwards to avoid mixing in the comments). Here’s a shell command to add 72 to the menu so you don’t have to drag to select each time:

  ```lsl
  $ defaults write com.macromates.textmate OakWrapColumns '( 40, 72, 78 )'

  ```

  More important than the mechanics of formatting the body is the practice of having a subject line. As the example indicates, you should shoot for about 50 characters (though this isn’t a hard maximum) and always, always follow it with a blank line. This first line should be a concise summary of the changes introduced by the commit; if there are any technical details that cannot be expressed in these strict size constraints, put them in the body instead. The subject line is used all over Git, oftentimes in truncated form if too long of a message was used. The following are just a handful of examples of where it ends up:

  * `git log --pretty=oneline` shows a terse history mapping containing the commit id and the summary
  * `git rebase --interactive` provides the summary for each commit in the editor it invokes
  * if the config option `merge.summary` is set, the summaries from all merged commits will make their way into the merge commit message
  * `git shortlog` uses summary lines in the changelog-like output it produces
  * `git format-patch`, `git send-email`, and related tools use it as the subject for emails
  * reflogs, a local history accessible with `git reflog` intended to help you recover from stupid mistakes, get a copy of the summary
  * `gitk` has a column for the summary
  * GitHub uses the summary in various places in their user interface

  The subject/body distinction may seem unimportant but it’s one of many subtle factors that makes Git history so much more pleasant to work with than Subversion.

metadata:
  {"date_added":"2023-11-21 11:59:10","id":"01b3b6c1-27ae-40a4-99fa-d297febc1e7c","status":"ARCHIVED","tags":["read-later"],"title":"tbaggery - A Note About Git Commit Messages\n","url_omnivore":"https://omnivore.app/me/https-tbaggery-com-2008-04-19-a-note-about-git-commit-messages-h-18bf2646727\n","url_original":"https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html\n"}


path: Omnivore/2023-12-24 - Historias de usuario - Ejemplos y plantilla - Atlassian.md
sortable_path: Omnivore2023-12-24 - Historias de usuario - Ejemplos y plantilla - Atlassian.md
title: Historias de usuario | Ejemplos y plantilla | Atlassian
lead: # Historias de usuario | Ejemplos y plantilla | Atlassian
checksum: 575dd080faa9b75ce65156e0dd67b3173d03ff59a339c178ddf4cf92e8e47628
created: 2024-09-15 00:40:57.687758783+00:00
modified: 2024-09-22 22:40:13.39451104+00:00
body:
  # Historias de usuario | Ejemplos y plantilla | Atlassian

  ## Highlights

  una historia de usuario es una explicación general e informal de una función de software escrita desde la perspectiva del usuario final. Su propósito es articular cómo proporcionará una función de software valor al cliente. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#d1988eb3-4939-4bf3-b932-44aad141c065) 

  ---

  Las historias encajan perfectamente en marcos ágiles como [scrum](https://www.atlassian.com/es/agile/scrum) y [kanban](https://www.atlassian.com/es/agile/kanban). En el scrum, las historias de los usuarios se añaden a los sprints y se van realizando a lo largo del sprint. Los equipos de kanban incorporan las historias de usuario en su backlog y las ejecutan siguiendo su flujo de trabajo. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#3e9e29ab-43db-4c8f-b517-c2f3b8892432) 

  ---

  Las historias de usuario son también los componentes básicos de los marcos ágiles más grandes, como los epics y las iniciativas. Los epics son grandes elementos de trabajo divididos en un conjunto de historias, y varios epics constituyen una iniciativa. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#8f41f654-4b72-4789-b848-b46c2d185291) 

  ---

  ## Cómo escribir historias de usuario 

  Piensa en lo siguiente cuando escribas historias de usuario:

  * **Definición de “Listo”**: la historia suele estar “lista” cuando el usuario puede completar la tarea descrita, pero debes asegurarte de definir lo que representa completarla.
  * **Describe tareas o subtareas**: decide qué pasos específicos deben completarse y quién es responsable de cada uno de ellos.
  * **Perfiles de usuario**: ¿para quién? Si hay varios usuarios finales, considera crear varias historias.
  * **Pasos ordenados**: escribe una historia para cada paso en un proceso más grande.
  * **Escucha el feedback**: habla con los usuarios y capta sus problemas o necesidades en lo que dicen. No es necesario tener que estar adivinando las historias cuando puedes obtenerlas de tus clientes.
  * **Tiempo**: el tiempo es un tema delicado. Muchos equipos de desarrollo evitan hablar sobre el tiempo, y en su lugar confían en sus marcos de trabajo de estimación. Dado que las historias deberían completarse en un sprint, aquellas que puedan necesitar semanas o meses deberían dividirse en historias más pequeñas o considerarse un epic independiente. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#536ca974-d30c-4d48-a02f-a9c16c469e01) 

  ---

  Las historias de usuario suelen expresarse con una frase simple con la siguiente estructura:

  **“Como \[perfil\], \[quiero\] \[para\].”**

  Desglosemos esta estructura: 

  * “Como \[perfil\]”: ¿para quién desarrollamos esto? No solo buscamos un puesto, buscamos el perfil de la persona. Max. Nuestro equipo debería comprender quién es Max. Con suerte hemos entrevistado a muchos Max. Comprendemos cómo trabaja esa persona, cómo piensa y cómo se siente. Sentimos empatía por Max.
  * “Quiere”: aquí describimos su intención, no las funciones que usan. ¿Qué es lo que están intentando lograr realmente? Esta descripción debería realizarse con independencia de las implementaciones; si describes algún elemento de la IU y no el objetivo del usuario, estás cometiendo un error.
  * “Para”: ¿cómo encaja su deseo inmediato de hacer algo en la perspectiva general? ¿Cuál es el beneficio general que intentan lograr? ¿Cuál es el gran problema que debe resolverse? 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#74f6051c-6394-4c99-94c9-176857d9caf2) 

  ---

  ## Original

  * [DevOps](#)  
     * [Entrega continua](https://www.atlassian.com/es/continuous-delivery)  
     * [Git](https://www.atlassian.com/es/git)
  * [Agile](https://www.atlassian.com/es/agile)
  * [Microservicios](https://www.atlassian.com/es/microservices)

  Las historias de usuario son tareas de desarrollo que se suelen expresar como "persona + necesidad + propósito".

  Resumen: _==una historia de usuario es una explicación general e informal de una función de software escrita desde la perspectiva del usuario final. Su propósito es articular cómo proporcionará una función de software valor al cliente.==_

  Es tentador pensar que las historias de usuario son, en pocas palabras, requisitos del sistema de software. Pero no lo son. 

  Un componente clave del desarrollo de software ágil es poner a las personas en primer lugar, y las historias de usuarios ponen a los usuarios finales reales en el centro de la conversación. Las historias utilizan un lenguaje no técnico para ofrecer contexto al equipo de desarrollo y sus esfuerzos. Después de leer una historia de usuario, el equipo sabe por qué está compilando lo que está compilando y qué valor crea. 

  Las historias de usuario son uno de los componentes centrales de un programa ágil. Ayudan a proporcionar un marco centrado en el usuario para el trabajo diario, lo que impulsa la colaboración y la creatividad y mejora el producto en general.

  ##  ¿Qué son las historias de usuario ágiles? 

  Una historia de usuario es la unidad de trabajo más pequeña en un marco ágil. Es un objetivo final, no una función, expresado desde la perspectiva del usuario del software.

  Una historia de usuario es una explicación general e informal de una función de software escrita desde la perspectiva del usuario final o cliente. 

  El propósito de una historia de usuario es articular cómo un elemento de trabajo entregará un valor particular al cliente. Ten en cuenta que los "clientes" no tienen por qué ser usuarios finales externos en el sentido tradicional, también pueden ser clientes internos o colegas dentro de tu organización que dependen de tu equipo.

  Las historias de usuario son unas pocas frases en lenguaje sencillo que describen el resultado deseado. No entran en detalles, ya que los requisitos se añaden más tarde, una vez acordados por el equipo.

  ==Las historias encajan perfectamente en marcos ágiles como== ==[scrum](https://www.atlassian.com/es/agile/scrum)== ==y== ==[kanban](https://www.atlassian.com/es/agile/kanban)====. En el scrum, las historias de los usuarios se añaden a los sprints y se van realizando a lo largo del sprint. Los equipos de kanban incorporan las historias de usuario en su backlog y las ejecutan siguiendo su flujo de trabajo.== Es este trabajo sobre las historias de usuario lo que ayuda a los equipos de scrum a mejorar en la [estimación](https://www.atlassian.com/es/agile/project-management/estimation) y planificación de sprints, lo que conduce a un pronóstico más preciso y a una mayor agilidad. Gracias a las historias, los equipos de kanban aprenden a gestionar el trabajo en curso (WIP) y pueden perfeccionar aún más sus flujos de trabajo.

  ==Las historias de usuario son también los componentes básicos de los marcos ágiles más grandes, como los epics y las iniciativas. Los epics son grandes elementos de trabajo divididos en un conjunto de historias, y varios epics constituyen una iniciativa.== Estas estructuras más grandes garantizan que el trabajo diario del equipo de desarrollo contribuya a los objetivos de la organización incorporados en los epics y las iniciativas.

  [Más información sobre epics e iniciativas](https://www.atlassian.com/es/agile/project-management/epics-stories-themes)

  ![Epics frente a historias y frente a temas ágiles | Orientador ágil de Atlassian](https://proxy-prod.omnivore-image-cache.app/0x0,sW2bi5Evxhti5v5nDhvukDixoEeUjIrdTwJc6L7dtKiA/https://wac-cdn.atlassian.com/dam/jcr:a679339b-0098-4c88-acdb-7009b0de6efb/epics-vs-stories-agile-development.png?cdnVersion=1373) 

  ##  ¿Por qué crear historias de usuario? 

  Para los equipos de desarrollo nuevos en la metodología ágil, las historias de usuario a veces parecen un paso más. ¿Por qué no dividir el gran proyecto ([el epic](https://www.atlassian.com/es/agile/project-management/epics)) en una serie de pasos y seguir adelante? Pero las historias dan al equipo un contexto importante y asocian las tareas con el valor que estas aportan.

  Las historias de usuario tienen varios beneficios clave:

  * **Las historias centran la atención en el usuario.** Una lista de tareas pendientes mantiene al equipo centrado en tareas que deben completarse, pero un conjunto de historias lo mantiene centrado en solucionar problemas para usuarios reales.
  * **Las historias permiten la colaboración.** Con el objetivo definido, el equipo puede colaborar para decidir cómo ofrecer un mejor servicio al usuario y cumplir con dicho objetivo.
  * **Las historias impulsan soluciones creativas.** Las historias fomentan que el equipo piense de forma crítica y creativa sobre cómo lograr mejor un objetivo.
  * **Las historias motivan.** Con cada historia el equipo de desarrollo disfruta de un pequeño reto y una pequeña victoria, lo que aumenta la motivación.

  ##  Trabajar con historias de usuario 

  Una vez que se ha escrito una historia, es hora de integrarla en tu flujo de trabajo. Por lo general, una historia la escribe el propietario del producto, el gestor del producto o el gestor del programa, y la envía para su revisión.

  Durante una reunión de planificación de sprint o iteración, el equipo decide qué historias afrontará en ese sprint. Los equipos discuten los requisitos y la funcionalidad que requiere cada historia de usuario. Esta es una oportunidad para ponerse técnico y creativo en la implementación de la historia por parte del equipo. Una vez acordados, estos requisitos se añaden a la historia.

  Otro paso común en esta reunión es calificar las historias en función de su complejidad o tiempo hasta su finalización. Los equipos usan las tallas de las camisetas, la secuencia de Fibonacci o el Planning Poker para hacer las estimaciones adecuadas. Una historia debe ser de un tamaño que pueda completarse en un sprint; por lo tanto, cuando el equipo establezca las especificaciones de cada historia, se deben asegurar de dividir las historias que superen ese horizonte de finalización. 

  ## ==Cómo escribir historias de usuario== 

  ==Piensa en lo siguiente cuando escribas historias de usuario:==

  * **==Definición de “Listo”==**==: la historia suele estar “lista” cuando el usuario puede completar la tarea descrita, pero debes asegurarte de definir lo que representa completarla.==
  * **==Describe tareas o subtareas==**==: decide qué pasos específicos deben completarse y quién es responsable de cada uno de ellos.==
  * **==Perfiles de usuario==**==: ¿para quién? Si hay varios usuarios finales, considera crear varias historias.==
  * **==Pasos ordenados==**==: escribe una historia para cada paso en un proceso más grande.==
  * **==Escucha el feedback==**==: habla con los usuarios y capta sus problemas o necesidades en lo que dicen. No es necesario tener que estar adivinando las historias cuando puedes obtenerlas de tus clientes.==
  * **==Tiempo==**==: el tiempo es un tema delicado. Muchos equipos de desarrollo evitan hablar sobre el tiempo, y en su lugar confían en sus marcos de trabajo de estimación. Dado que las historias deberían completarse en un sprint, aquellas que puedan necesitar semanas o meses deberían dividirse en historias más pequeñas o considerarse un epic independiente.==

  Una vez que las historias de usuario estén definidas de forma clara, debes asegurarte de que todo el equipo pueda verlas.

  ==Las historias de usuario suelen expresarse con una frase simple con la siguiente estructura:==

  **==“Como [perfil], [quiero] [para].”==**

  ==Desglosemos esta estructura:== 

  * ==“Como [perfil]”: ¿para quién desarrollamos esto? No solo buscamos un puesto, buscamos el perfil de la persona. Max. Nuestro equipo debería comprender quién es Max. Con suerte hemos entrevistado a muchos Max. Comprendemos cómo trabaja esa persona, cómo piensa y cómo se siente. Sentimos empatía por Max.==
  * ==“Quiere”: aquí describimos su intención, no las funciones que usan. ¿Qué es lo que están intentando lograr realmente? Esta descripción debería realizarse con independencia de las implementaciones; si describes algún elemento de la IU y no el objetivo del usuario, estás cometiendo un error.==
  * ==“Para”: ¿cómo encaja su deseo inmediato de hacer algo en la perspectiva general? ¿Cuál es el beneficio general que intentan lograr? ¿Cuál es el gran problema que debe resolverse?==

  Por ejemplo, las historias de usuario pueden tener este aspecto:

  * Como Max, quiero invitar a mis amigos, para que podamos disfrutar de este servicio juntos.
  * Como Sascha, quiero organizar mi trabajo, para poder sentir que tengo un mayor control.
  * Como gestor, quiero poder comprender el progreso de mis compañeros, para poder informar sobre nuestros éxitos y fallos.

  Esta estructura no es obligatoria, pero resulta de ayuda para establecer una definición de "hecho". Cuando ese perfil puede alcanzar su valor deseado, la historia está completa. Recomendamos a nuestros equipos definir su propia estructura, y que no se desvíen de ella.

  ##  Introducción a las historias de usuario ágiles 

  Las historias de los usuarios describen el por qué y el qué que hay detrás del trabajo diario de los miembros del equipo de desarrollo; a menudo las historias de usuario se expresan de la siguiente manera: _perfil + necesidad + propósito_. Entender su papel como fuente de verdad para lo que el equipo está entregando, pero también el por qué, es clave para un proceso sin problemas.

  Empieza por evaluar el siguiente gran proyecto o el más apremiante (por ejemplo, un epic). Divídelo en historias de usuario más pequeñas y trabaja con el equipo de desarrollo para mejorarlo. Una vez que tus historias están fuera, donde todo el equipo puede verlas, ya tienes todo listo para empezar a trabajar.

  ![Max Rehkopf](https://proxy-prod.omnivore-image-cache.app/0x0,syT9pntR4CaQYCcIqba5ypD-oikurrflhJTjV7sO1ExQ/https://wac-cdn.atlassian.com/es/dam/jcr:ba03a215-2f45-40f5-8540-b2015223c918/Max-R_Headshot%20(1).jpg?cdnVersion=1373) 

   Max Rehkopf 

  Como persona caótica que soy, confío en las prácticas de la metodología ágil y en los principios optimizados para poner orden en mi día a día. Me alegra compartir estas lecciones con otras personas a través de los muchos artículos, ponencias y vídeos que hago para Atlassian. 

  ![](https://proxy-prod.omnivore-image-cache.app/0x0,sl_ZksgNpNXWZfy83BHaRWg43nNL2e3UI5RkRcT2a1T0/https://wac-cdn.atlassian.com/dam/jcr:c185cc4f-0ee1-4ed0-bd7d-cd77fef8a8d8/ScrumTutorial.svg?cdnVersion=1373)

  tutorial 

  #### Cómo crear historias de usuario en Jira Software

  Descubre cómo los equipos pueden utilizar los tiques para realizar un seguimiento del trabajo que debe completarse. 

  [Probar el tutorial ](https://www.atlassian.com/es/agile/tutorials/issues) 

  ![](https://proxy-prod.omnivore-image-cache.app/0x0,sayxceodUXJRoop6GCfPrE2jGieRSxdK-gPbtyLUKwNw/https://wac-cdn.atlassian.com/dam/jcr:689d8fe7-3859-41cc-b3b4-92004cd7b7af/ProjectManagementTitle.png?cdnVersion=1373)

  artículo 

  #### ¿Qué son los puntos de historia y cómo se estiman?

  Conoce por dentro los secretos de la estimación ágil y los puntos de historia. Una buena estimación ágil permite a los propietarios del producto optimizar sus procesos en términos de eficiencia e impacto.

  [Leer el artículo ](https://www.atlassian.com/es/agile/project-management/estimation)

raw_content:
  ---
  id: a82f2e4b-829e-48a5-9e5c-c152860743a7
  title: |
    Historias de usuario | Ejemplos y plantilla | Atlassian
  status: ARCHIVED
  tags:
    - read-later
  date_added: 2023-12-24 18:35:22
  url_omnivore: |
    https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1
  url_original: |
    https://www.atlassian.com/es/agile/project-management/user-stories
  ---

  # Historias de usuario | Ejemplos y plantilla | Atlassian

  ## Highlights

  una historia de usuario es una explicación general e informal de una función de software escrita desde la perspectiva del usuario final. Su propósito es articular cómo proporcionará una función de software valor al cliente. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#d1988eb3-4939-4bf3-b932-44aad141c065) 

  ---

  Las historias encajan perfectamente en marcos ágiles como [scrum](https://www.atlassian.com/es/agile/scrum) y [kanban](https://www.atlassian.com/es/agile/kanban). En el scrum, las historias de los usuarios se añaden a los sprints y se van realizando a lo largo del sprint. Los equipos de kanban incorporan las historias de usuario en su backlog y las ejecutan siguiendo su flujo de trabajo. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#3e9e29ab-43db-4c8f-b517-c2f3b8892432) 

  ---

  Las historias de usuario son también los componentes básicos de los marcos ágiles más grandes, como los epics y las iniciativas. Los epics son grandes elementos de trabajo divididos en un conjunto de historias, y varios epics constituyen una iniciativa. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#8f41f654-4b72-4789-b848-b46c2d185291) 

  ---

  ## Cómo escribir historias de usuario 

  Piensa en lo siguiente cuando escribas historias de usuario:

  * **Definición de “Listo”**: la historia suele estar “lista” cuando el usuario puede completar la tarea descrita, pero debes asegurarte de definir lo que representa completarla.
  * **Describe tareas o subtareas**: decide qué pasos específicos deben completarse y quién es responsable de cada uno de ellos.
  * **Perfiles de usuario**: ¿para quién? Si hay varios usuarios finales, considera crear varias historias.
  * **Pasos ordenados**: escribe una historia para cada paso en un proceso más grande.
  * **Escucha el feedback**: habla con los usuarios y capta sus problemas o necesidades en lo que dicen. No es necesario tener que estar adivinando las historias cuando puedes obtenerlas de tus clientes.
  * **Tiempo**: el tiempo es un tema delicado. Muchos equipos de desarrollo evitan hablar sobre el tiempo, y en su lugar confían en sus marcos de trabajo de estimación. Dado que las historias deberían completarse en un sprint, aquellas que puedan necesitar semanas o meses deberían dividirse en historias más pequeñas o considerarse un epic independiente. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#536ca974-d30c-4d48-a02f-a9c16c469e01) 

  ---

  Las historias de usuario suelen expresarse con una frase simple con la siguiente estructura:

  **“Como \[perfil\], \[quiero\] \[para\].”**

  Desglosemos esta estructura: 

  * “Como \[perfil\]”: ¿para quién desarrollamos esto? No solo buscamos un puesto, buscamos el perfil de la persona. Max. Nuestro equipo debería comprender quién es Max. Con suerte hemos entrevistado a muchos Max. Comprendemos cómo trabaja esa persona, cómo piensa y cómo se siente. Sentimos empatía por Max.
  * “Quiere”: aquí describimos su intención, no las funciones que usan. ¿Qué es lo que están intentando lograr realmente? Esta descripción debería realizarse con independencia de las implementaciones; si describes algún elemento de la IU y no el objetivo del usuario, estás cometiendo un error.
  * “Para”: ¿cómo encaja su deseo inmediato de hacer algo en la perspectiva general? ¿Cuál es el beneficio general que intentan lograr? ¿Cuál es el gran problema que debe resolverse? 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1#74f6051c-6394-4c99-94c9-176857d9caf2) 

  ---

  ## Original

  * [DevOps](#)  
     * [Entrega continua](https://www.atlassian.com/es/continuous-delivery)  
     * [Git](https://www.atlassian.com/es/git)
  * [Agile](https://www.atlassian.com/es/agile)
  * [Microservicios](https://www.atlassian.com/es/microservices)

  Las historias de usuario son tareas de desarrollo que se suelen expresar como "persona + necesidad + propósito".

  Resumen: _==una historia de usuario es una explicación general e informal de una función de software escrita desde la perspectiva del usuario final. Su propósito es articular cómo proporcionará una función de software valor al cliente.==_

  Es tentador pensar que las historias de usuario son, en pocas palabras, requisitos del sistema de software. Pero no lo son. 

  Un componente clave del desarrollo de software ágil es poner a las personas en primer lugar, y las historias de usuarios ponen a los usuarios finales reales en el centro de la conversación. Las historias utilizan un lenguaje no técnico para ofrecer contexto al equipo de desarrollo y sus esfuerzos. Después de leer una historia de usuario, el equipo sabe por qué está compilando lo que está compilando y qué valor crea. 

  Las historias de usuario son uno de los componentes centrales de un programa ágil. Ayudan a proporcionar un marco centrado en el usuario para el trabajo diario, lo que impulsa la colaboración y la creatividad y mejora el producto en general.

  ##  ¿Qué son las historias de usuario ágiles? 

  Una historia de usuario es la unidad de trabajo más pequeña en un marco ágil. Es un objetivo final, no una función, expresado desde la perspectiva del usuario del software.

  Una historia de usuario es una explicación general e informal de una función de software escrita desde la perspectiva del usuario final o cliente. 

  El propósito de una historia de usuario es articular cómo un elemento de trabajo entregará un valor particular al cliente. Ten en cuenta que los "clientes" no tienen por qué ser usuarios finales externos en el sentido tradicional, también pueden ser clientes internos o colegas dentro de tu organización que dependen de tu equipo.

  Las historias de usuario son unas pocas frases en lenguaje sencillo que describen el resultado deseado. No entran en detalles, ya que los requisitos se añaden más tarde, una vez acordados por el equipo.

  ==Las historias encajan perfectamente en marcos ágiles como== ==[scrum](https://www.atlassian.com/es/agile/scrum)== ==y== ==[kanban](https://www.atlassian.com/es/agile/kanban)====. En el scrum, las historias de los usuarios se añaden a los sprints y se van realizando a lo largo del sprint. Los equipos de kanban incorporan las historias de usuario en su backlog y las ejecutan siguiendo su flujo de trabajo.== Es este trabajo sobre las historias de usuario lo que ayuda a los equipos de scrum a mejorar en la [estimación](https://www.atlassian.com/es/agile/project-management/estimation) y planificación de sprints, lo que conduce a un pronóstico más preciso y a una mayor agilidad. Gracias a las historias, los equipos de kanban aprenden a gestionar el trabajo en curso (WIP) y pueden perfeccionar aún más sus flujos de trabajo.

  ==Las historias de usuario son también los componentes básicos de los marcos ágiles más grandes, como los epics y las iniciativas. Los epics son grandes elementos de trabajo divididos en un conjunto de historias, y varios epics constituyen una iniciativa.== Estas estructuras más grandes garantizan que el trabajo diario del equipo de desarrollo contribuya a los objetivos de la organización incorporados en los epics y las iniciativas.

  [Más información sobre epics e iniciativas](https://www.atlassian.com/es/agile/project-management/epics-stories-themes)

  ![Epics frente a historias y frente a temas ágiles | Orientador ágil de Atlassian](https://proxy-prod.omnivore-image-cache.app/0x0,sW2bi5Evxhti5v5nDhvukDixoEeUjIrdTwJc6L7dtKiA/https://wac-cdn.atlassian.com/dam/jcr:a679339b-0098-4c88-acdb-7009b0de6efb/epics-vs-stories-agile-development.png?cdnVersion=1373) 

  ##  ¿Por qué crear historias de usuario? 

  Para los equipos de desarrollo nuevos en la metodología ágil, las historias de usuario a veces parecen un paso más. ¿Por qué no dividir el gran proyecto ([el epic](https://www.atlassian.com/es/agile/project-management/epics)) en una serie de pasos y seguir adelante? Pero las historias dan al equipo un contexto importante y asocian las tareas con el valor que estas aportan.

  Las historias de usuario tienen varios beneficios clave:

  * **Las historias centran la atención en el usuario.** Una lista de tareas pendientes mantiene al equipo centrado en tareas que deben completarse, pero un conjunto de historias lo mantiene centrado en solucionar problemas para usuarios reales.
  * **Las historias permiten la colaboración.** Con el objetivo definido, el equipo puede colaborar para decidir cómo ofrecer un mejor servicio al usuario y cumplir con dicho objetivo.
  * **Las historias impulsan soluciones creativas.** Las historias fomentan que el equipo piense de forma crítica y creativa sobre cómo lograr mejor un objetivo.
  * **Las historias motivan.** Con cada historia el equipo de desarrollo disfruta de un pequeño reto y una pequeña victoria, lo que aumenta la motivación.

  ##  Trabajar con historias de usuario 

  Una vez que se ha escrito una historia, es hora de integrarla en tu flujo de trabajo. Por lo general, una historia la escribe el propietario del producto, el gestor del producto o el gestor del programa, y la envía para su revisión.

  Durante una reunión de planificación de sprint o iteración, el equipo decide qué historias afrontará en ese sprint. Los equipos discuten los requisitos y la funcionalidad que requiere cada historia de usuario. Esta es una oportunidad para ponerse técnico y creativo en la implementación de la historia por parte del equipo. Una vez acordados, estos requisitos se añaden a la historia.

  Otro paso común en esta reunión es calificar las historias en función de su complejidad o tiempo hasta su finalización. Los equipos usan las tallas de las camisetas, la secuencia de Fibonacci o el Planning Poker para hacer las estimaciones adecuadas. Una historia debe ser de un tamaño que pueda completarse en un sprint; por lo tanto, cuando el equipo establezca las especificaciones de cada historia, se deben asegurar de dividir las historias que superen ese horizonte de finalización. 

  ## ==Cómo escribir historias de usuario== 

  ==Piensa en lo siguiente cuando escribas historias de usuario:==

  * **==Definición de “Listo”==**==: la historia suele estar “lista” cuando el usuario puede completar la tarea descrita, pero debes asegurarte de definir lo que representa completarla.==
  * **==Describe tareas o subtareas==**==: decide qué pasos específicos deben completarse y quién es responsable de cada uno de ellos.==
  * **==Perfiles de usuario==**==: ¿para quién? Si hay varios usuarios finales, considera crear varias historias.==
  * **==Pasos ordenados==**==: escribe una historia para cada paso en un proceso más grande.==
  * **==Escucha el feedback==**==: habla con los usuarios y capta sus problemas o necesidades en lo que dicen. No es necesario tener que estar adivinando las historias cuando puedes obtenerlas de tus clientes.==
  * **==Tiempo==**==: el tiempo es un tema delicado. Muchos equipos de desarrollo evitan hablar sobre el tiempo, y en su lugar confían en sus marcos de trabajo de estimación. Dado que las historias deberían completarse en un sprint, aquellas que puedan necesitar semanas o meses deberían dividirse en historias más pequeñas o considerarse un epic independiente.==

  Una vez que las historias de usuario estén definidas de forma clara, debes asegurarte de que todo el equipo pueda verlas.

  ==Las historias de usuario suelen expresarse con una frase simple con la siguiente estructura:==

  **==“Como [perfil], [quiero] [para].”==**

  ==Desglosemos esta estructura:== 

  * ==“Como [perfil]”: ¿para quién desarrollamos esto? No solo buscamos un puesto, buscamos el perfil de la persona. Max. Nuestro equipo debería comprender quién es Max. Con suerte hemos entrevistado a muchos Max. Comprendemos cómo trabaja esa persona, cómo piensa y cómo se siente. Sentimos empatía por Max.==
  * ==“Quiere”: aquí describimos su intención, no las funciones que usan. ¿Qué es lo que están intentando lograr realmente? Esta descripción debería realizarse con independencia de las implementaciones; si describes algún elemento de la IU y no el objetivo del usuario, estás cometiendo un error.==
  * ==“Para”: ¿cómo encaja su deseo inmediato de hacer algo en la perspectiva general? ¿Cuál es el beneficio general que intentan lograr? ¿Cuál es el gran problema que debe resolverse?==

  Por ejemplo, las historias de usuario pueden tener este aspecto:

  * Como Max, quiero invitar a mis amigos, para que podamos disfrutar de este servicio juntos.
  * Como Sascha, quiero organizar mi trabajo, para poder sentir que tengo un mayor control.
  * Como gestor, quiero poder comprender el progreso de mis compañeros, para poder informar sobre nuestros éxitos y fallos.

  Esta estructura no es obligatoria, pero resulta de ayuda para establecer una definición de "hecho". Cuando ese perfil puede alcanzar su valor deseado, la historia está completa. Recomendamos a nuestros equipos definir su propia estructura, y que no se desvíen de ella.

  ##  Introducción a las historias de usuario ágiles 

  Las historias de los usuarios describen el por qué y el qué que hay detrás del trabajo diario de los miembros del equipo de desarrollo; a menudo las historias de usuario se expresan de la siguiente manera: _perfil + necesidad + propósito_. Entender su papel como fuente de verdad para lo que el equipo está entregando, pero también el por qué, es clave para un proceso sin problemas.

  Empieza por evaluar el siguiente gran proyecto o el más apremiante (por ejemplo, un epic). Divídelo en historias de usuario más pequeñas y trabaja con el equipo de desarrollo para mejorarlo. Una vez que tus historias están fuera, donde todo el equipo puede verlas, ya tienes todo listo para empezar a trabajar.

  ![Max Rehkopf](https://proxy-prod.omnivore-image-cache.app/0x0,syT9pntR4CaQYCcIqba5ypD-oikurrflhJTjV7sO1ExQ/https://wac-cdn.atlassian.com/es/dam/jcr:ba03a215-2f45-40f5-8540-b2015223c918/Max-R_Headshot%20(1).jpg?cdnVersion=1373) 

   Max Rehkopf 

  Como persona caótica que soy, confío en las prácticas de la metodología ágil y en los principios optimizados para poner orden en mi día a día. Me alegra compartir estas lecciones con otras personas a través de los muchos artículos, ponencias y vídeos que hago para Atlassian. 

  ![](https://proxy-prod.omnivore-image-cache.app/0x0,sl_ZksgNpNXWZfy83BHaRWg43nNL2e3UI5RkRcT2a1T0/https://wac-cdn.atlassian.com/dam/jcr:c185cc4f-0ee1-4ed0-bd7d-cd77fef8a8d8/ScrumTutorial.svg?cdnVersion=1373)

  tutorial 

  #### Cómo crear historias de usuario en Jira Software

  Descubre cómo los equipos pueden utilizar los tiques para realizar un seguimiento del trabajo que debe completarse. 

  [Probar el tutorial ](https://www.atlassian.com/es/agile/tutorials/issues) 

  ![](https://proxy-prod.omnivore-image-cache.app/0x0,sayxceodUXJRoop6GCfPrE2jGieRSxdK-gPbtyLUKwNw/https://wac-cdn.atlassian.com/dam/jcr:689d8fe7-3859-41cc-b3b4-92004cd7b7af/ProjectManagementTitle.png?cdnVersion=1373)

  artículo 

  #### ¿Qué son los puntos de historia y cómo se estiman?

  Conoce por dentro los secretos de la estimación ágil y los puntos de historia. Una buena estimación ágil permite a los propietarios del producto optimizar sus procesos en términos de eficiencia e impacto.

  [Leer el artículo ](https://www.atlassian.com/es/agile/project-management/estimation) 

metadata:
  {"date_added":"2023-12-24 18:35:22","id":"a82f2e4b-829e-48a5-9e5c-c152860743a7","status":"ARCHIVED","tags":["read-later"],"title":"Historias de usuario | Ejemplos y plantilla | Atlassian\n","url_omnivore":"https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-user-stories-18c9dc0ecd1\n","url_original":"https://www.atlassian.com/es/agile/project-management/user-stories\n"}


path: Omnivore/2023-12-24 - ¿Qué son los puntos de historia en la metodología ágil y cómo se estiman-.md
sortable_path: Omnivore2023-12-24 - ¿Qué son los puntos de historia en la metodología ágil y cómo se estiman-.md
title: ¿Qué son los puntos de historia en la metodología ágil y cómo se estiman?
lead: # ¿Qué son los puntos de historia en la metodología ágil y cómo se estiman?
checksum: c59ff69a1ec0b872667f382a2e861a77d6dbf38d38eeba190d8e55793b2103ae
created: 2024-09-15 00:40:57.743205254+00:00
modified: 2024-09-22 22:40:13.39451104+00:00
body:
  # ¿Qué son los puntos de historia en la metodología ágil y cómo se estiman?

  ## Highlights

  Se trata de unidades de medida que permiten expresar una estimación del esfuerzo total que deberá hacer el equipo para implementar íntegramente un elemento del backlog del producto o cualquier otro trabajo. Los equipos asignan puntos de historia en función de la complejidad y del volumen del trabajo, así como del riesgo o de la incertidumbre. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-estimation-18c9dc1e48a#4ddb962e-4ff6-44fa-8e72-b74db4a9b7a2) 

  ---

  ## Original

  * [DevOps](#)  
     * [Entrega continua](https://www.atlassian.com/es/continuous-delivery)  
     * [Git](https://www.atlassian.com/es/git)
  * [Agile](https://www.atlassian.com/es/agile)
  * [Microservicios](https://www.atlassian.com/es/microservices)

  ##  Puntos de historia y estimación 

  Una buena estimación ayuda a los propietarios de los productos a optimizar sus procesos en términos de eficiencia e impacto. Por eso es tan importante.

  Hacer estimaciones es complicado. Para los desarrolladores de software, es uno de los aspectos más difíciles de su trabajo, por no decir el más difícil. Conlleva tener en cuenta un montón de factores que ayudan a los propietarios de los productos a tomar decisiones que afectan a todo el equipo, así como a la empresa. Con todo eso en juego, no es de extrañar que todos, desde los desarrolladores hasta la alta dirección, tiendan a perder los estribos sobre este tema. Craso error. La estimación ágil de los puntos de historia no es más que eso, un cálculo: no es un pacto de sangre. 

  No es obligatorio trabajar los fines de semana para compensar el tiempo de más que nos lleva un trabajo que habíamos subestimado. Dicho eso, veamos algunas maneras de realizar estimaciones con la mayor precisión posible.

  ##  Colaboración con el propietario del producto 

  En un desarrollo ágil, el [propietario del producto](https://www.atlassian.com/es/agile/product-management) se encarga de priorizar el [backlog](https://www.atlassian.com/es/agile/scrum/backlogs), es decir, la lista ordenada de trabajo que contiene descripciones breves de todas las funciones y correcciones de un producto. Los propietarios del producto capturan los [requisitos](https://www.atlassian.com/es/agile/product-management/requirements) empresariales, pero no siempre entienden los detalles de la implementación. Por ello, una buena estimación puede informar al propietario del producto sobre el nivel de esfuerzo de cada elemento de trabajo, que a su vez sirve para evaluar la prioridad relativa de cada elemento.

  Cuando el equipo de ingeniería empieza su proceso de estimación, normalmente surgen preguntas sobre los requisitos y las historias de usuario. Esto es algo positivo: las preguntas ayudan a todo el equipo a entender el trabajo mejor. Específicamente en el caso de los propietarios de los productos, la división granular de los elementos de trabajo y las estimaciones les ayudan a priorizar todas las áreas del trabajo, incluidas las que pueden estar ocultas. Con las estimaciones del equipo de desarrollo en la mano, no es extraño que un propietario del producto reordene los elementos del backlog.

  ##  La estimación ágil de los puntos de historia es un trabajo en equipo 

  Involucrar a todo el mundo (desarrolladores, diseñadores, testers, deployers... todos) en el equipo es clave. Cada miembro del equipo aporta una perspectiva diferente sobre el producto y el trabajo necesario para entregar una historia de usuario. Por ejemplo, si la gestión de productos quiere hacer algo que parece sencillo, como admitir un nuevo navegador web, el desarrollo y el control de calidad deben dar su opinión también, ya que su experiencia les ha enseñado qué dragones pueden estar al acecho bajo la superficie.

  Asimismo, los cambios de diseño requieren no sólo la aportación del equipo de diseño, sino también la del de desarrollo y la del de QA. Dejar a parte del equipo de producto más amplio fuera del proceso de estimación crea estimaciones de menor calidad, baja la moral porque los contribuyentes clave no se sienten incluidos y compromete la calidad del software.

  No dejes que tu equipo sea víctima de las estimaciones poco precisas. Es un camino seguro al fracaso.

  ##  Puntos de historia frente a horas 

  Los equipos de software tradicionales proporcionan estimaciones en un formato de tiempo concreto: pueden ser días, semanas o meses. Sin embargo, muchos equipos ágiles han decidido pasarse a los puntos de historia. ==Se trata de unidades de medida que permiten expresar una estimación del esfuerzo total que deberá hacer el equipo para implementar íntegramente un elemento del backlog del producto o cualquier otro trabajo. Los equipos asignan puntos de historia en función de la complejidad y del volumen del trabajo, así como del riesgo o de la incertidumbre.== Los valores se asignan para desglosar el trabajo de forma más eficaz en partes más pequeñas. De esta manera, se puede gestionar la incertidumbre. Con el tiempo, esto ayuda a los equipos a ser conscientes de lo que pueden llegar a conseguir en un período de tiempo concreto y genera un sentimiento de consenso y compromiso con la solución. Aunque pueda parecer contradictorio, esta abstracción es realmente útil, ya que obliga al equipo a tomar decisiones más complejas sobre la dificultad del trabajo. A continuación, se indican algunos motivos por los cuales es recomendable utilizar puntos de historia:

  * Las fechas no tienen en cuenta el trabajo no relacionado con el proyecto que inevitablemente surge en nuestro día a día, como correos electrónicos, reuniones y entrevistas en las que un miembro del equipo puede participar.
  * Las fechas tienen una connotación emocional. La estimación relativa elimina este componente.
  * Cada equipo estima el trabajo en una escala ligeramente diferente, lo cual significa que su velocidad (medida en puntos) será diferente, como es natural. Asimismo, esto imposibilita que se politiquee usando la velocidad como arma.
  * Una vez que se llegue a un acuerdo sobre el esfuerzo relativo del valor de cada punto de historia, podrás asignar puntos rápidamente sin que haya lugar a demasiado debate.
  * Los puntos de historia recompensan a los miembros del equipo por resolver incidencias basándose en la dificultad, y no en el tiempo empleado. De esta forma, los miembros del equipo se mantienen centrados en entregar valor, no en el tiempo dedicado.

  Lamentablemente, los puntos de historia se suelen utilizar de forma incorrecta; por ejemplo, cuando se emplean para juzgar a las personas o para asignar cronogramas y recursos detallados, o bien cuando se confunden con una medida de productividad. La auténtica función de los puntos de historia es que los equipos puedan hacerse una idea del volumen de trabajo y saber qué partes tienen prioridad. Para ver un debate en profundidad sobre los puntos de historia y las prácticas relacionadas con las estimaciones, échale un vistazo a esta [mesa redonda con expertos del sector](https://community.atlassian.com/t5/Agile-articles/Six-experts-sound-off-on-story-points-the-evolution-of-agile/ba-p/1553590). Si quieres más consejos sobre la estimación ágil, sigue leyendo.

  [ ](https://www.youtube.com/watch?v=%5FN5gj9gzOjg) 

  ##  Puntos de historia y póker de planificación 

  Los equipos que se están iniciando en los puntos de historia usan un ejercicio llamado [Planning Poker](https://www.atlassian.com/blog/platform/a-brief-overview-of-planning-poker). En Atlassian, el Planning Poker es una práctica habitual en toda la empresa. Los miembros del equipo toman un elemento del backlog, hablan sobre él brevemente y cada uno fórmula mentalmente una estimación. A continuación, todos levantan una tarjeta con el número que refleje su estimación. Si todo el mundo está de acuerdo, ¡estupendo! De lo contrario, dedica algo de tiempo (no mucho, tan solo un par de minutos) para entender el motivo de las distintas estimaciones. Recuerda, sin embargo, que la estimación debe ser una actividad bastante general. Si el equipo se va por las ramas, respira hondo y deriva el debate a un superior.

  **¿Listo para intentarlo?** 

  * Instala esta [Aplicación de póker de planificación](https://marketplace.atlassian.com/apps/1212495/planning-poker?hosting=cloud&tab=overview)
  * Obtén más información sobre el [póker de planificación](https://www.atlassian.com/blog/agile/planning-poker-sane-healthy)

  ##  Estima con mayor inteligencia, no con mayor esfuerzo 

  Ninguna tarea individual debe superar las 16 horas de trabajo. (Si usas puntos de historia, puedes decidir que 20 puntos es el límite superior, por ejemplo). Sencillamente, es demasiado complicado estimar elementos de trabajo individuales de mayor duración con confianza. Esa confianza es especialmente importante para los elementos en la parte superior del backlog. Cuando algo se estima por encima del límite de 16 horas (o 20 puntos) del equipo, será una señal para dividirlo granularmente y volver a estimarlo.

  Para los elementos que se encuentren más abajo en el backlog, basta con una estimación aproximada. Cuando el equipo empiece a trabajar en esos elementos, los requisitos podrían haber cambiado y la aplicación seguramente habrá cambiado también, de modo que las estimaciones no serán tan precisas. No pierdas tiempo estimando trabajo que posiblemente cambiará. Da al propietario del producto una cifra aproximada que pueda utilizar para priorizar la hoja de ruta del producto adecuadamente.

  ##  Aprende de las estimaciones anteriores 

  Las retrospectivas constituyen un momento para que el equipo incorpore ideas de iteraciones anteriores, incluida la precisión de sus estimaciones. Hay muchas herramientas ágiles (como [Jira Software](https://www.atlassian.com/es/software/jira)) que realizan el seguimiento de los puntos de historia, cosa que facilita en gran medida el análisis y el recalibrado de las estimaciones. Prueba, por ejemplo, a comparar las cinco últimas historias de usuario que haya entregado el equipo con un valor de 8 puntos de historia. Estudia si cada uno de estos elementos de trabajo tuvo un nivel de esfuerzo similar. Si no, analizad por qué. Utilizad esta información en los siguientes debates de estimaciones.

  Al igual que [el resto de los aspectos de un proceso ágil](https://www.atlassian.com/es/agile/project-management), la estimación es una cuestión de práctica. Irás mejorando con el tiempo. 

  ![Dan Radigan](https://proxy-prod.omnivore-image-cache.app/0x0,s9OKxpW9uX89PL44D0A_DX8yh1LxH26TPVUB3WHhB_W4/https://wac-cdn.atlassian.com/es/dam/jcr:2a395b8a-4485-4727-80c1-82fb787b4479/Dan_Radigan_200x200.png?cdnVersion=1373) 

   Dan Radigan 

  La metodología ágil ha influido mucho en mí, tanto en el aspecto profesional como en el personal: he aprendido que las mejores experiencias se basan en el modelo ágil, tanto al programar como en la vida real. Mis intereses suelen moverse entre la tecnología, la fotografía y el motociclismo. 

  ![](https://proxy-prod.omnivore-image-cache.app/0x0,sl_ZksgNpNXWZfy83BHaRWg43nNL2e3UI5RkRcT2a1T0/https://wac-cdn.atlassian.com/dam/jcr:c185cc4f-0ee1-4ed0-bd7d-cd77fef8a8d8/ScrumTutorial.svg?cdnVersion=1373)

  tutorial 

  #### Aprender a usar diagramas de trabajo pendiente con Jira Software

  La guía completa sobre diagramas de evolución en Jira Software. Aprende a supervisar epics y sprints con diagramas de evolución.

  [Probar el tutorial ](https://www.atlassian.com/es/agile/tutorials/burndown-charts) 

  ![](https://proxy-prod.omnivore-image-cache.app/0x0,sayxceodUXJRoop6GCfPrE2jGieRSxdK-gPbtyLUKwNw/https://wac-cdn.atlassian.com/dam/jcr:689d8fe7-3859-41cc-b3b4-92004cd7b7af/ProjectManagementTitle.png?cdnVersion=1373)

  artículo 

  #### Cinco métricas ágiles que no odiarás

  ¿Cómo usar métricas ágiles? Descubre las gráficas de trabajo pendiente de sprints, epics y publicaciones, los gráficos de control y velocidad, y el diagrama de flujo acumulado.

  [Leer el artículo ](https://www.atlassian.com/es/agile/project-management/metrics)

raw_content:
  ---
  id: 22bb5f59-fddb-4ab4-89dd-bb28fe723f4f
  title: |
    ¿Qué son los puntos de historia en la metodología ágil y cómo se estiman?
  status: ARCHIVED
  tags:
    - read-later
  date_added: 2023-12-24 18:36:25
  url_omnivore: |
    https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-estimation-18c9dc1e48a
  url_original: |
    https://www.atlassian.com/es/agile/project-management/estimation
  ---

  # ¿Qué son los puntos de historia en la metodología ágil y cómo se estiman?

  ## Highlights

  Se trata de unidades de medida que permiten expresar una estimación del esfuerzo total que deberá hacer el equipo para implementar íntegramente un elemento del backlog del producto o cualquier otro trabajo. Los equipos asignan puntos de historia en función de la complejidad y del volumen del trabajo, así como del riesgo o de la incertidumbre. 

  [source](https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-estimation-18c9dc1e48a#4ddb962e-4ff6-44fa-8e72-b74db4a9b7a2) 

  ---

  ## Original

  * [DevOps](#)  
     * [Entrega continua](https://www.atlassian.com/es/continuous-delivery)  
     * [Git](https://www.atlassian.com/es/git)
  * [Agile](https://www.atlassian.com/es/agile)
  * [Microservicios](https://www.atlassian.com/es/microservices)

  ##  Puntos de historia y estimación 

  Una buena estimación ayuda a los propietarios de los productos a optimizar sus procesos en términos de eficiencia e impacto. Por eso es tan importante.

  Hacer estimaciones es complicado. Para los desarrolladores de software, es uno de los aspectos más difíciles de su trabajo, por no decir el más difícil. Conlleva tener en cuenta un montón de factores que ayudan a los propietarios de los productos a tomar decisiones que afectan a todo el equipo, así como a la empresa. Con todo eso en juego, no es de extrañar que todos, desde los desarrolladores hasta la alta dirección, tiendan a perder los estribos sobre este tema. Craso error. La estimación ágil de los puntos de historia no es más que eso, un cálculo: no es un pacto de sangre. 

  No es obligatorio trabajar los fines de semana para compensar el tiempo de más que nos lleva un trabajo que habíamos subestimado. Dicho eso, veamos algunas maneras de realizar estimaciones con la mayor precisión posible.

  ##  Colaboración con el propietario del producto 

  En un desarrollo ágil, el [propietario del producto](https://www.atlassian.com/es/agile/product-management) se encarga de priorizar el [backlog](https://www.atlassian.com/es/agile/scrum/backlogs), es decir, la lista ordenada de trabajo que contiene descripciones breves de todas las funciones y correcciones de un producto. Los propietarios del producto capturan los [requisitos](https://www.atlassian.com/es/agile/product-management/requirements) empresariales, pero no siempre entienden los detalles de la implementación. Por ello, una buena estimación puede informar al propietario del producto sobre el nivel de esfuerzo de cada elemento de trabajo, que a su vez sirve para evaluar la prioridad relativa de cada elemento.

  Cuando el equipo de ingeniería empieza su proceso de estimación, normalmente surgen preguntas sobre los requisitos y las historias de usuario. Esto es algo positivo: las preguntas ayudan a todo el equipo a entender el trabajo mejor. Específicamente en el caso de los propietarios de los productos, la división granular de los elementos de trabajo y las estimaciones les ayudan a priorizar todas las áreas del trabajo, incluidas las que pueden estar ocultas. Con las estimaciones del equipo de desarrollo en la mano, no es extraño que un propietario del producto reordene los elementos del backlog.

  ##  La estimación ágil de los puntos de historia es un trabajo en equipo 

  Involucrar a todo el mundo (desarrolladores, diseñadores, testers, deployers... todos) en el equipo es clave. Cada miembro del equipo aporta una perspectiva diferente sobre el producto y el trabajo necesario para entregar una historia de usuario. Por ejemplo, si la gestión de productos quiere hacer algo que parece sencillo, como admitir un nuevo navegador web, el desarrollo y el control de calidad deben dar su opinión también, ya que su experiencia les ha enseñado qué dragones pueden estar al acecho bajo la superficie.

  Asimismo, los cambios de diseño requieren no sólo la aportación del equipo de diseño, sino también la del de desarrollo y la del de QA. Dejar a parte del equipo de producto más amplio fuera del proceso de estimación crea estimaciones de menor calidad, baja la moral porque los contribuyentes clave no se sienten incluidos y compromete la calidad del software.

  No dejes que tu equipo sea víctima de las estimaciones poco precisas. Es un camino seguro al fracaso.

  ##  Puntos de historia frente a horas 

  Los equipos de software tradicionales proporcionan estimaciones en un formato de tiempo concreto: pueden ser días, semanas o meses. Sin embargo, muchos equipos ágiles han decidido pasarse a los puntos de historia. ==Se trata de unidades de medida que permiten expresar una estimación del esfuerzo total que deberá hacer el equipo para implementar íntegramente un elemento del backlog del producto o cualquier otro trabajo. Los equipos asignan puntos de historia en función de la complejidad y del volumen del trabajo, así como del riesgo o de la incertidumbre.== Los valores se asignan para desglosar el trabajo de forma más eficaz en partes más pequeñas. De esta manera, se puede gestionar la incertidumbre. Con el tiempo, esto ayuda a los equipos a ser conscientes de lo que pueden llegar a conseguir en un período de tiempo concreto y genera un sentimiento de consenso y compromiso con la solución. Aunque pueda parecer contradictorio, esta abstracción es realmente útil, ya que obliga al equipo a tomar decisiones más complejas sobre la dificultad del trabajo. A continuación, se indican algunos motivos por los cuales es recomendable utilizar puntos de historia:

  * Las fechas no tienen en cuenta el trabajo no relacionado con el proyecto que inevitablemente surge en nuestro día a día, como correos electrónicos, reuniones y entrevistas en las que un miembro del equipo puede participar.
  * Las fechas tienen una connotación emocional. La estimación relativa elimina este componente.
  * Cada equipo estima el trabajo en una escala ligeramente diferente, lo cual significa que su velocidad (medida en puntos) será diferente, como es natural. Asimismo, esto imposibilita que se politiquee usando la velocidad como arma.
  * Una vez que se llegue a un acuerdo sobre el esfuerzo relativo del valor de cada punto de historia, podrás asignar puntos rápidamente sin que haya lugar a demasiado debate.
  * Los puntos de historia recompensan a los miembros del equipo por resolver incidencias basándose en la dificultad, y no en el tiempo empleado. De esta forma, los miembros del equipo se mantienen centrados en entregar valor, no en el tiempo dedicado.

  Lamentablemente, los puntos de historia se suelen utilizar de forma incorrecta; por ejemplo, cuando se emplean para juzgar a las personas o para asignar cronogramas y recursos detallados, o bien cuando se confunden con una medida de productividad. La auténtica función de los puntos de historia es que los equipos puedan hacerse una idea del volumen de trabajo y saber qué partes tienen prioridad. Para ver un debate en profundidad sobre los puntos de historia y las prácticas relacionadas con las estimaciones, échale un vistazo a esta [mesa redonda con expertos del sector](https://community.atlassian.com/t5/Agile-articles/Six-experts-sound-off-on-story-points-the-evolution-of-agile/ba-p/1553590). Si quieres más consejos sobre la estimación ágil, sigue leyendo.

  [ ](https://www.youtube.com/watch?v=%5FN5gj9gzOjg) 

  ##  Puntos de historia y póker de planificación 

  Los equipos que se están iniciando en los puntos de historia usan un ejercicio llamado [Planning Poker](https://www.atlassian.com/blog/platform/a-brief-overview-of-planning-poker). En Atlassian, el Planning Poker es una práctica habitual en toda la empresa. Los miembros del equipo toman un elemento del backlog, hablan sobre él brevemente y cada uno fórmula mentalmente una estimación. A continuación, todos levantan una tarjeta con el número que refleje su estimación. Si todo el mundo está de acuerdo, ¡estupendo! De lo contrario, dedica algo de tiempo (no mucho, tan solo un par de minutos) para entender el motivo de las distintas estimaciones. Recuerda, sin embargo, que la estimación debe ser una actividad bastante general. Si el equipo se va por las ramas, respira hondo y deriva el debate a un superior.

  **¿Listo para intentarlo?** 

  * Instala esta [Aplicación de póker de planificación](https://marketplace.atlassian.com/apps/1212495/planning-poker?hosting=cloud&tab=overview)
  * Obtén más información sobre el [póker de planificación](https://www.atlassian.com/blog/agile/planning-poker-sane-healthy)

  ##  Estima con mayor inteligencia, no con mayor esfuerzo 

  Ninguna tarea individual debe superar las 16 horas de trabajo. (Si usas puntos de historia, puedes decidir que 20 puntos es el límite superior, por ejemplo). Sencillamente, es demasiado complicado estimar elementos de trabajo individuales de mayor duración con confianza. Esa confianza es especialmente importante para los elementos en la parte superior del backlog. Cuando algo se estima por encima del límite de 16 horas (o 20 puntos) del equipo, será una señal para dividirlo granularmente y volver a estimarlo.

  Para los elementos que se encuentren más abajo en el backlog, basta con una estimación aproximada. Cuando el equipo empiece a trabajar en esos elementos, los requisitos podrían haber cambiado y la aplicación seguramente habrá cambiado también, de modo que las estimaciones no serán tan precisas. No pierdas tiempo estimando trabajo que posiblemente cambiará. Da al propietario del producto una cifra aproximada que pueda utilizar para priorizar la hoja de ruta del producto adecuadamente.

  ##  Aprende de las estimaciones anteriores 

  Las retrospectivas constituyen un momento para que el equipo incorpore ideas de iteraciones anteriores, incluida la precisión de sus estimaciones. Hay muchas herramientas ágiles (como [Jira Software](https://www.atlassian.com/es/software/jira)) que realizan el seguimiento de los puntos de historia, cosa que facilita en gran medida el análisis y el recalibrado de las estimaciones. Prueba, por ejemplo, a comparar las cinco últimas historias de usuario que haya entregado el equipo con un valor de 8 puntos de historia. Estudia si cada uno de estos elementos de trabajo tuvo un nivel de esfuerzo similar. Si no, analizad por qué. Utilizad esta información en los siguientes debates de estimaciones.

  Al igual que [el resto de los aspectos de un proceso ágil](https://www.atlassian.com/es/agile/project-management), la estimación es una cuestión de práctica. Irás mejorando con el tiempo. 

  ![Dan Radigan](https://proxy-prod.omnivore-image-cache.app/0x0,s9OKxpW9uX89PL44D0A_DX8yh1LxH26TPVUB3WHhB_W4/https://wac-cdn.atlassian.com/es/dam/jcr:2a395b8a-4485-4727-80c1-82fb787b4479/Dan_Radigan_200x200.png?cdnVersion=1373) 

   Dan Radigan 

  La metodología ágil ha influido mucho en mí, tanto en el aspecto profesional como en el personal: he aprendido que las mejores experiencias se basan en el modelo ágil, tanto al programar como en la vida real. Mis intereses suelen moverse entre la tecnología, la fotografía y el motociclismo. 

  ![](https://proxy-prod.omnivore-image-cache.app/0x0,sl_ZksgNpNXWZfy83BHaRWg43nNL2e3UI5RkRcT2a1T0/https://wac-cdn.atlassian.com/dam/jcr:c185cc4f-0ee1-4ed0-bd7d-cd77fef8a8d8/ScrumTutorial.svg?cdnVersion=1373)

  tutorial 

  #### Aprender a usar diagramas de trabajo pendiente con Jira Software

  La guía completa sobre diagramas de evolución en Jira Software. Aprende a supervisar epics y sprints con diagramas de evolución.

  [Probar el tutorial ](https://www.atlassian.com/es/agile/tutorials/burndown-charts) 

  ![](https://proxy-prod.omnivore-image-cache.app/0x0,sayxceodUXJRoop6GCfPrE2jGieRSxdK-gPbtyLUKwNw/https://wac-cdn.atlassian.com/dam/jcr:689d8fe7-3859-41cc-b3b4-92004cd7b7af/ProjectManagementTitle.png?cdnVersion=1373)

  artículo 

  #### Cinco métricas ágiles que no odiarás

  ¿Cómo usar métricas ágiles? Descubre las gráficas de trabajo pendiente de sprints, epics y publicaciones, los gráficos de control y velocidad, y el diagrama de flujo acumulado.

  [Leer el artículo ](https://www.atlassian.com/es/agile/project-management/metrics) 

metadata:
  {"date_added":"2023-12-24 18:36:25","id":"22bb5f59-fddb-4ab4-89dd-bb28fe723f4f","status":"ARCHIVED","tags":["read-later"],"title":"¿Qué son los puntos de historia en la metodología ágil y cómo se estiman?\n","url_omnivore":"https://omnivore.app/me/https-www-atlassian-com-es-agile-project-management-estimation-18c9dc1e48a\n","url_original":"https://www.atlassian.com/es/agile/project-management/estimation\n"}


title: Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React
lead: # Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React
checksum: 9fba65b69cc43378f301f5365cffde37353898e45b830e25cf30922c43efc82b
created: 2024-09-15 00:40:57.782891099+00:00
modified: 2024-09-22 22:40:13.39451104+00:00
path:
  Omnivore/2024-02-12 - Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React.md

sortable_path:
  Omnivore2024-02-12 - Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React.md

body:
  # Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React

  ## Highlights

  Debouncing accepts a function and transforms it in to an updated (debounced) function so that the code inside the original function is executed after a certain period of time. 

  [source](https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510#2c8f31bd-f011-49bd-99bc-36192f7fd823) 

  ---

  function debounce(func, delay) {let timeout=null return (...args) => {if(timeout) clearTimeout(timeout) timeout=setTimeout(() \=> { func(...args) timeout=null }, delay) } } 

  [source](https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510#5a57c802-520a-409a-a51c-e554a6ec8bd5) 

  ---

  const useDebounce = (func, delay) => { let timeout\=null return (...args) => {if(timeout) clearTimeout(timeout)timeout\=setTimeout(() => { func(...args) }, delay) } }export default useDebounce 

  [source](https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510#226d8c7b-6900-4b2f-a705-5f5b6e10afc5) 

  ---

  ## Original

  ![Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React](https://proxy-prod.omnivore-image-cache.app/2000x1333,st8a8yNqtoznuGhyo0BqzXJQ0cEap88iHr4kvlN3Ff1Y/https://www.freecodecamp.org/news/content/images/size/w2000/2024/02/photo-1550063873-ab792950096b.jpeg) 

  Hi readers, I hope you are doing great! I am back with another tutorial on web development. If you are someone who enjoys developing web apps with JavaScript and React, then this post is for you.

  When you roll out a new app into production, you want to make sure that it's user friendly. A website's performance is a key part of the user experience. Every user wants the website and its contents to load quickly. Each and every second is valuable and could result into a user never visiting your website again.

  In this guide, we are going to understand a very important technique in JavaScript known as debouncing. Then, I will show you how to implement the autocomplete functionality in React with debouncing. 

  Now, in order to get the most out of this tutorial, I am assuming you have a basic knowledge of JavaScript. If you need to get started or review, here are a couple resources for you:

  * Learn JavaScript basics – [handbook for beginners](https://www.freecodecamp.org/news/learn-javascript-for-beginners/)
  * The freeCodeCamp [JavaScript Algorithms and Data Structures certification](https://www.freecodecamp.org/news/learn-javascript-with-new-data-structures-and-algorithms-certification-projects/)

  ## **Table of Contents:**

  * [What is Debouncing?](#what-is-debouncing)
  * [How to Implement Debouncing in JavaScript](#how-to-implement-debouncing-in-javascript)
  * [Use Case of Debouncing](#use-case-of-debouncing)
  * [Conclusion](#conclusion)

  ## What is Debouncing?

  Debouncing is a strategy used to improve the performance of a feature by controlling the time at which a function should be executed. 

  ==Debouncing accepts a function and transforms it in to an updated (debounced) function so that the code inside the original function is executed after a certain period of time.== 

  If the debounced function is called again within that period, the previous timer is reset and a new timer is started for this function call. The process repeats for each function call. 

  An example will help you understand better. Let's take a function `fun()`. We want this function to execute after 500ms.

  ```crystal
  function fun() {
      console.log('This is a function')
  }
  ```

  After debouncing, a new function `debouncedFun()` is returned. Now, whenever you call `debouncedFun()`, it will be called after 500ms. 

  If you call it again within the next 500ms after first calling it, the previous timer is reset and a new timer is started for the second function call. The process repeats if you keep calling the function within 500ms.

  ## How to Implement Debouncing in JavaScript

  Let's understand how to implement debouncing in JavaScript. First, we'll go over our requirements. What behavior do we want from the debounced function?

  * Delay the function execution by a certain time, `delay`.
  * Reset the timer if the function is called again.

  To debounce a function, we'll have a separate function that accepts the function reference and the delay as parameters, and returns a debounced function.

  ```ada
  function debounce(func, delay) {
        return () => {}   // return debounced function
  }
  ```

  This function will only be called once to return a debounced function and that, in turn, will be used in the subsequent code.

  To delay a function by some milliseconds, we can simply use the `setTimeout` function in JavaScript.

  ```arcade
  function debounce(func, delay) {
      return () => {
          setTimeout(() => {
          	func()
          }, delay)
      }
  }
  ```

  This delays the function call by `delay` milliseconds. But this is incomplete as it only satisfies the first requirement. How do we achieve the second behaviour?

  Let's create a variable `timeout` and assign it to the return value of `setTimeout` method. The `setTimeout` method returns a unique identifier to the timeout, which is held by `timeout` variable.

  ```javascript
  function debounce(func, delay) {
      let timeout=null
      return () => {
          timeout=setTimeout(() => {
              func()
          }, delay)
      }
  }
  ```

  Each time you invoke `setTimeout`, the ID is different. We will use this `timeout` variable to reset the timer.

  But how do we get access to `timeout` from outside the `debounce()` method? As mentioned before, `debounce()` is only used once to return a debounced function. This, in turn, performs the debouncing logic.

  Then, how does the debounced function have access to `timeout` even if it is used outside the `debounce()` function? Well, it uses a concept called closure. 

  ### What's a closure in JavaScript?

  In JavaScript, an inner function always has access to the local variables of the outer function. In our case, the inner function has access to `timeout` that has function level scope in the `debounce()` method.

  But when the outer function returns this inner function, the inner function still holds a reference to the local variables of the outer function long after the outer function has finished execution. This is the concept of a closure. 

  Let's understand closures with an example.

  ```javascript
  function outerFunction() {
    const x = 5;

    return () => {
      console.log(x);
    }
  }

  const inner = outerFunction();

  inner(); // prints 5

  // console.log(x)   Throws reference error
  ```

  Here, if we call `inner()`, the code runs without any errors and prints 5\. But, if we try to access `x` directly, JavaScript throws a reference error.

  ![Screenshot-2024-02-09-141749](https://proxy-prod.omnivore-image-cache.app/691x199,sr83BH8L2pwjWfvF3o3ztAC_Fg8t5J_j8jc8rZM45-I8/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-141749.png)

  JavaScript Reference Error

  Here, `inner()` closes over `x` and only this function can use the variable and no one other one can. We cannot access the variable explicitly.

  You can check out [this beginner-friendly tutorial](https://www.freecodecamp.org/news/closures-in-javascript/) to learn more about closures.

  ### Back to Debouncing

  Let's get back to where we left off:

  ```javascript
  function debounce(func, delay) {
      let timeout=null
      return () => {
          timeout=setTimeout(() => {
              func()
          }, delay)
      }
  }
  ```

  Here, JavaScript uses a closure to hold access to `timeout` every time we use the debounced function. 

  Let's use this to our advantage. Since `debouncedFun()` has access to the same `timeout` variable in every function call, we can add a condition to check whether a previous timeout exists. We can simply do this with a null check, `if(timeout !== null)` or `if(timeout)`.

  Then, we use the `clearTimeout()` method to cancel the previous timeout, thus resetting the timer. 

  Add the following statement before starting a new timeout:

  ```lisp
  if(timeout) 
  	clearTimeout(timeout)

  ```

  Once the timeout is reset, a new timeout is started for the current function call, whose ID is then assigned to `timeout`. The process is repeated for the subsequent function calls who have access to the same `timeout` due to closures.

  ```javascript
  function debounce(func, delay) {
      let timeout=null
      return () => {
          if(timeout) clearTimeout(timeout)

          timeout=setTimeout(() => {
              func()
          }, delay)
      }
  }
  ```

  With this, we have satisfied our second requirement – that is, resetting the timer and starting a new one. It's time to use this debounced function.

  Let's pass `fun()` to the `debounce()` method with a delay of 500ms.

  ```kotlin
  const debouncedFun = debounce(fun, 500)

  ```

  `debouncedFun()` is basically `fun()` with debouncing behaviour. Let's call this function at different time intervals to test our functionality.

  ```stylus
  debouncedFun()

  setTimeout(debouncedFun, 300)

  setTimeout(debouncedFun, 900)
  ```

  The first function call is made instantly. The other two are made after 300ms and 900ms respectively. Can you guess the output?

  The code prints `This is a function` two times. Let's understand why. Here, after the first call is made, `fun()` is scheduled to execute after 500ms. But the second one is made in 300ms which resets the timer and starts a new one.

  500ms have passed and the `fun()` method executes. Then, at 900ms, another function call is made. This again executes `fun()` after 500ms.

  There is still a small improvement we should make. Our logic does not consider function arguments. Let's replace `fun()` with `fun(a, b)`.

  ```javascript
  function fun(a, b) {
      console.log(`This is a function with arguments ${a} and ${b}`)
  }
  ```

  To incorporate arguments while debouncing, return a debounced function that accepts arguments.

  ```javascript
  function debounce(func, delay) {
      let timeout=null
      return (...args) => {
          if(timeout) clearTimeout(timeout)

          timeout=setTimeout(() => {
              func(...args)
              timeout=null
          }, delay)
      }
  }
  ```

  By using the spread operator, any arguments passed to the debounced function will be stored as an array in the `args` variable. Then, spread out the same `args` array to call the actual function with the arguments passed. 

  ```kotlin
  const debouncedFun=debounce(fun, 500)
  debouncedFun(2,3)
  ```

  The above code prints `This is a function with arguments 2 and 3` after 500ms.

  ## Use Case of Debouncing

  Let's see how debouncing is used in practical applications. The most common use case of debouncing is the autocomplete functionality. You must have seen many websites where you type into an input field and it shows a list of results as you type them. 

  Here's an example from Google Search:

  ![Screenshot-2024-02-09-163240](https://proxy-prod.omnivore-image-cache.app/1462x487,s2SiKOKcVhwfHi5VYnRJVYbQ5pPYw6Qo8YMUAIGDxKzs/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-163240.png)

  Google Search Autocomplete after typing in "Top 10"

  Google search shows the most recent and commonly searched terms. The information is mostly fetched from the browser cache. But, several websites make API calls to backend server to fetch the data from a database.

  This can easily be implemented by adding an `onchange` event to the `input` element and implementing the fetch logic in the event handler. But there's a slight issue with this.

  Consider the following example:

  ![Screenshot-2024-02-09-163930](https://proxy-prod.omnivore-image-cache.app/1915x882,sAcuTnmrT0AUE51eNXOrpzFo5aQn4QNd8NLdDhqrwkD0/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-163930.png)

  API Request made for each input value

  When I type the word _absolute_, an API request is made every time the value of the input field changes. We are making 8 API requests in very few milliseconds which puts a lot of load on the backend server and could cause performance issues. 

  Ideally, we want to show the auto-complete results some time after the user has finished typing. Here, the user has typed _absolute_ in one go, so instead of showing results every time the input changes, we could show them once the user has finished typing – that is, we could add some delay between the input change and the results being displayed.

  So, we only make the API calls when the user finishes typing their word and not on every input change. This reduces the number of API calls and improves performance. We can achieve this behavior with debouncing. 

  Let's understand how to implement the autocomplete functionality in React.

  ### Auto-complete example

  Use `create-react-app` (or a modern build tool like Vite) to create the project. Remove the existing boilerplate code. There is no need to install any additional dependencies. Run `npm start` command to start the project. You can find the complete code on [GitHub](https://github.com/KunalN25/react-debouncing). 

  I have set up a Node server to fetch data for the app. You can find it in the Git repo. Run the `node server` command to start it. I am not going to show the Node.js code as it's out of the scope of this tutorial.

  Let's get started with the implementation. We will write a simple autocomplete functionality. The app should show a list of cities that contain an input string typed by the user.

  #### App Component

  We'll first need an `input` element to accept user input and a _results container_ for the search results. Attach an event handler to the `input` element which is an `async` function since it will include the fetching logic. 

  ```javascript
  function App() {
      const [data, setData] = useState(null)

      const loadData = async (event) => {
          
      }
      return (
          <div className="App">
              <input type="text" onChange={(e) => loadData(e)}/>
              {data && data.length !== 0 &&
              <div className="results-container">
                  {data.map(item => (
                      <div key={item.id} className="result-item">
                          <p> {item.city} </p>
                      </div>
                  ))}
              </div>}
          </div>
      );
  }
  ```

  The data will be stored as state and the results will only be shown if the data is non-empty. I'll skip over the CSS for this tutorial, you can find it in the [Git Repo](https://github.com/KunalN25/react-debouncing).

  #### Event Handler

  The `loadData()` function fetches our data and stores the response as state.

  ```cs
  const loadData = async (event) => {
  	const value=event.target.value
      if(value === '') {
          setData(null)
          return
      }
      const response=await fetch(`http://localhost:8000/data/${value}`)
      const res=await response.json()
      setData(res)
  }
  ```

  If no value is entered, simply exit the function. Else, make the request to the node server endpoint. This function is called every time the input changes, so we will debounce this function.

  #### Debounce Implementation using a Custom Hook

  We will write the debouncing logic inside a custom hook. The advantage of custom hooks is that you can re-use the same logic throughout your application. It is highly advisable to do so.

  Create a new folder `custom-hooks` and inside it, create a file `useDebounce.js`. As explained before, the `useDebounce()` method should take a function and delay as parameters and return the debounced function.

  ```routeros
  const useDebounce = (func, delay) => {
      let timeout=null

      return (...args) => {
          if(timeout) clearTimeout(timeout)

          timeout=setTimeout(() => {
              func(...args)
          }, delay)
      }
  }

  export default useDebounce
  ```

  Now, inside the app component, call this method once to get `loadDataDebounced()`.

  ```angelscript
  const loadDataDebounced = useDebounce(loadData, 400)

  ```

  We'll use this new method as the event handler for the `input` element.

  ```reasonml
  <input type="text" onChange={(e) => loadDataDebounced(e)}/>

  ```

  #### Output

  Enter a search string inside the `input` element to test our code.

  ![Screenshot-2024-02-09-190240](https://proxy-prod.omnivore-image-cache.app/835x564,sb5zP6lXJrIxE_8ItjnafuRCnKNnHRzFsCN62XGBrLfQ/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-190240.png)

  On-screen output

  ![Screenshot-2024-02-09-191234](https://proxy-prod.omnivore-image-cache.app/1283x452,sHxsFT2-w6tk2KBgte1fwMNck9ai0l4qA7S8k1fzQDew/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-191234.png)

  As you can see in the Network tab, only one request is getting sent instead of three. This makes the search performance much better.

  ## Conclusion

  In this tutorial, you learned what debouncing is and how it is implemented. Debouncing delays the function execution by a certain time and resets the previous timer if the function is called again. 

  Debouncing uses the important concept of closures. I took a slight detour from the implementation to explain what closure is. It can be a confusing concept for beginners, so take your time understanding it. Closures allow you to work with local variables even after a function has finished execution. 

  After that, I showed you a popular use case of debouncing, the auto-complete functionality. The performance of the feature can be improved with debouncing. I also showed you how to implement auto-complete in React and use debouncing with custom hooks. I hope this helps you in future projects.

  If you are unable to understand the content or find the explanation unsatisfactory, let me know. New ideas are always appreciated! Feel free to connect with me on Twitter. Till then, Goodbye!

  ---

  ---

   Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. [Get started](https://www.freecodecamp.org/learn/)

raw_content:
  ---
  id: d529f41a-ca28-11ee-97f3-f78c291f6623
  title: |
    Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React
  status: ARCHIVED
  tags:
    - read-later
    - RSS
  date_added: 2024-02-12 20:23:30
  url_omnivore: |
    https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510
  url_original: |
    https://www.freecodecamp.org/news/deboucing-in-react-autocomplete-example/
  ---

  # Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React

  ## Highlights

  Debouncing accepts a function and transforms it in to an updated (debounced) function so that the code inside the original function is executed after a certain period of time. 

  [source](https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510#2c8f31bd-f011-49bd-99bc-36192f7fd823) 

  ---

  function debounce(func, delay) {let timeout=null return (...args) => {if(timeout) clearTimeout(timeout) timeout=setTimeout(() \=> { func(...args) timeout=null }, delay) } } 

  [source](https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510#5a57c802-520a-409a-a51c-e554a6ec8bd5) 

  ---

  const useDebounce = (func, delay) => { let timeout\=null return (...args) => {if(timeout) clearTimeout(timeout)timeout\=setTimeout(() => { func(...args) }, delay) } }export default useDebounce 

  [source](https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510#226d8c7b-6900-4b2f-a705-5f5b6e10afc5) 

  ---

  ## Original

  ![Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React](https://proxy-prod.omnivore-image-cache.app/2000x1333,st8a8yNqtoznuGhyo0BqzXJQ0cEap88iHr4kvlN3Ff1Y/https://www.freecodecamp.org/news/content/images/size/w2000/2024/02/photo-1550063873-ab792950096b.jpeg) 

  Hi readers, I hope you are doing great! I am back with another tutorial on web development. If you are someone who enjoys developing web apps with JavaScript and React, then this post is for you.

  When you roll out a new app into production, you want to make sure that it's user friendly. A website's performance is a key part of the user experience. Every user wants the website and its contents to load quickly. Each and every second is valuable and could result into a user never visiting your website again.

  In this guide, we are going to understand a very important technique in JavaScript known as debouncing. Then, I will show you how to implement the autocomplete functionality in React with debouncing. 

  Now, in order to get the most out of this tutorial, I am assuming you have a basic knowledge of JavaScript. If you need to get started or review, here are a couple resources for you:

  * Learn JavaScript basics – [handbook for beginners](https://www.freecodecamp.org/news/learn-javascript-for-beginners/)
  * The freeCodeCamp [JavaScript Algorithms and Data Structures certification](https://www.freecodecamp.org/news/learn-javascript-with-new-data-structures-and-algorithms-certification-projects/)

  ## **Table of Contents:**

  * [What is Debouncing?](#what-is-debouncing)
  * [How to Implement Debouncing in JavaScript](#how-to-implement-debouncing-in-javascript)
  * [Use Case of Debouncing](#use-case-of-debouncing)
  * [Conclusion](#conclusion)

  ## What is Debouncing?

  Debouncing is a strategy used to improve the performance of a feature by controlling the time at which a function should be executed. 

  ==Debouncing accepts a function and transforms it in to an updated (debounced) function so that the code inside the original function is executed after a certain period of time.== 

  If the debounced function is called again within that period, the previous timer is reset and a new timer is started for this function call. The process repeats for each function call. 

  An example will help you understand better. Let's take a function `fun()`. We want this function to execute after 500ms.

  ```crystal
  function fun() {
      console.log('This is a function')
  }
  ```

  After debouncing, a new function `debouncedFun()` is returned. Now, whenever you call `debouncedFun()`, it will be called after 500ms. 

  If you call it again within the next 500ms after first calling it, the previous timer is reset and a new timer is started for the second function call. The process repeats if you keep calling the function within 500ms.

  ## How to Implement Debouncing in JavaScript

  Let's understand how to implement debouncing in JavaScript. First, we'll go over our requirements. What behavior do we want from the debounced function?

  * Delay the function execution by a certain time, `delay`.
  * Reset the timer if the function is called again.

  To debounce a function, we'll have a separate function that accepts the function reference and the delay as parameters, and returns a debounced function.

  ```ada
  function debounce(func, delay) {
        return () => {}   // return debounced function
  }
  ```

  This function will only be called once to return a debounced function and that, in turn, will be used in the subsequent code.

  To delay a function by some milliseconds, we can simply use the `setTimeout` function in JavaScript.

  ```arcade
  function debounce(func, delay) {
      return () => {
          setTimeout(() => {
          	func()
          }, delay)
      }
  }
  ```

  This delays the function call by `delay` milliseconds. But this is incomplete as it only satisfies the first requirement. How do we achieve the second behaviour?

  Let's create a variable `timeout` and assign it to the return value of `setTimeout` method. The `setTimeout` method returns a unique identifier to the timeout, which is held by `timeout` variable.

  ```javascript
  function debounce(func, delay) {
      let timeout=null
      return () => {
          timeout=setTimeout(() => {
              func()
          }, delay)
      }
  }
  ```

  Each time you invoke `setTimeout`, the ID is different. We will use this `timeout` variable to reset the timer.

  But how do we get access to `timeout` from outside the `debounce()` method? As mentioned before, `debounce()` is only used once to return a debounced function. This, in turn, performs the debouncing logic.

  Then, how does the debounced function have access to `timeout` even if it is used outside the `debounce()` function? Well, it uses a concept called closure. 

  ### What's a closure in JavaScript?

  In JavaScript, an inner function always has access to the local variables of the outer function. In our case, the inner function has access to `timeout` that has function level scope in the `debounce()` method.

  But when the outer function returns this inner function, the inner function still holds a reference to the local variables of the outer function long after the outer function has finished execution. This is the concept of a closure. 

  Let's understand closures with an example.

  ```javascript
  function outerFunction() {
    const x = 5;

    return () => {
      console.log(x);
    }
  }

  const inner = outerFunction();

  inner(); // prints 5

  // console.log(x)   Throws reference error
  ```

  Here, if we call `inner()`, the code runs without any errors and prints 5\. But, if we try to access `x` directly, JavaScript throws a reference error.

  ![Screenshot-2024-02-09-141749](https://proxy-prod.omnivore-image-cache.app/691x199,sr83BH8L2pwjWfvF3o3ztAC_Fg8t5J_j8jc8rZM45-I8/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-141749.png)

  JavaScript Reference Error

  Here, `inner()` closes over `x` and only this function can use the variable and no one other one can. We cannot access the variable explicitly.

  You can check out [this beginner-friendly tutorial](https://www.freecodecamp.org/news/closures-in-javascript/) to learn more about closures.

  ### Back to Debouncing

  Let's get back to where we left off:

  ```javascript
  function debounce(func, delay) {
      let timeout=null
      return () => {
          timeout=setTimeout(() => {
              func()
          }, delay)
      }
  }
  ```

  Here, JavaScript uses a closure to hold access to `timeout` every time we use the debounced function. 

  Let's use this to our advantage. Since `debouncedFun()` has access to the same `timeout` variable in every function call, we can add a condition to check whether a previous timeout exists. We can simply do this with a null check, `if(timeout !== null)` or `if(timeout)`.

  Then, we use the `clearTimeout()` method to cancel the previous timeout, thus resetting the timer. 

  Add the following statement before starting a new timeout:

  ```lisp
  if(timeout) 
  	clearTimeout(timeout)

  ```

  Once the timeout is reset, a new timeout is started for the current function call, whose ID is then assigned to `timeout`. The process is repeated for the subsequent function calls who have access to the same `timeout` due to closures.

  ```javascript
  function debounce(func, delay) {
      let timeout=null
      return () => {
          if(timeout) clearTimeout(timeout)

          timeout=setTimeout(() => {
              func()
          }, delay)
      }
  }
  ```

  With this, we have satisfied our second requirement – that is, resetting the timer and starting a new one. It's time to use this debounced function.

  Let's pass `fun()` to the `debounce()` method with a delay of 500ms.

  ```kotlin
  const debouncedFun = debounce(fun, 500)

  ```

  `debouncedFun()` is basically `fun()` with debouncing behaviour. Let's call this function at different time intervals to test our functionality.

  ```stylus
  debouncedFun()

  setTimeout(debouncedFun, 300)

  setTimeout(debouncedFun, 900)
  ```

  The first function call is made instantly. The other two are made after 300ms and 900ms respectively. Can you guess the output?

  The code prints `This is a function` two times. Let's understand why. Here, after the first call is made, `fun()` is scheduled to execute after 500ms. But the second one is made in 300ms which resets the timer and starts a new one.

  500ms have passed and the `fun()` method executes. Then, at 900ms, another function call is made. This again executes `fun()` after 500ms.

  There is still a small improvement we should make. Our logic does not consider function arguments. Let's replace `fun()` with `fun(a, b)`.

  ```javascript
  function fun(a, b) {
      console.log(`This is a function with arguments ${a} and ${b}`)
  }
  ```

  To incorporate arguments while debouncing, return a debounced function that accepts arguments.

  ```javascript
  function debounce(func, delay) {
      let timeout=null
      return (...args) => {
          if(timeout) clearTimeout(timeout)

          timeout=setTimeout(() => {
              func(...args)
              timeout=null
          }, delay)
      }
  }
  ```

  By using the spread operator, any arguments passed to the debounced function will be stored as an array in the `args` variable. Then, spread out the same `args` array to call the actual function with the arguments passed. 

  ```kotlin
  const debouncedFun=debounce(fun, 500)
  debouncedFun(2,3)
  ```

  The above code prints `This is a function with arguments 2 and 3` after 500ms.

  ## Use Case of Debouncing

  Let's see how debouncing is used in practical applications. The most common use case of debouncing is the autocomplete functionality. You must have seen many websites where you type into an input field and it shows a list of results as you type them. 

  Here's an example from Google Search:

  ![Screenshot-2024-02-09-163240](https://proxy-prod.omnivore-image-cache.app/1462x487,s2SiKOKcVhwfHi5VYnRJVYbQ5pPYw6Qo8YMUAIGDxKzs/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-163240.png)

  Google Search Autocomplete after typing in "Top 10"

  Google search shows the most recent and commonly searched terms. The information is mostly fetched from the browser cache. But, several websites make API calls to backend server to fetch the data from a database.

  This can easily be implemented by adding an `onchange` event to the `input` element and implementing the fetch logic in the event handler. But there's a slight issue with this.

  Consider the following example:

  ![Screenshot-2024-02-09-163930](https://proxy-prod.omnivore-image-cache.app/1915x882,sAcuTnmrT0AUE51eNXOrpzFo5aQn4QNd8NLdDhqrwkD0/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-163930.png)

  API Request made for each input value

  When I type the word _absolute_, an API request is made every time the value of the input field changes. We are making 8 API requests in very few milliseconds which puts a lot of load on the backend server and could cause performance issues. 

  Ideally, we want to show the auto-complete results some time after the user has finished typing. Here, the user has typed _absolute_ in one go, so instead of showing results every time the input changes, we could show them once the user has finished typing – that is, we could add some delay between the input change and the results being displayed.

  So, we only make the API calls when the user finishes typing their word and not on every input change. This reduces the number of API calls and improves performance. We can achieve this behavior with debouncing. 

  Let's understand how to implement the autocomplete functionality in React.

  ### Auto-complete example

  Use `create-react-app` (or a modern build tool like Vite) to create the project. Remove the existing boilerplate code. There is no need to install any additional dependencies. Run `npm start` command to start the project. You can find the complete code on [GitHub](https://github.com/KunalN25/react-debouncing). 

  I have set up a Node server to fetch data for the app. You can find it in the Git repo. Run the `node server` command to start it. I am not going to show the Node.js code as it's out of the scope of this tutorial.

  Let's get started with the implementation. We will write a simple autocomplete functionality. The app should show a list of cities that contain an input string typed by the user.

  #### App Component

  We'll first need an `input` element to accept user input and a _results container_ for the search results. Attach an event handler to the `input` element which is an `async` function since it will include the fetching logic. 

  ```javascript
  function App() {
      const [data, setData] = useState(null)

      const loadData = async (event) => {
          
      }
      return (
          <div className="App">
              <input type="text" onChange={(e) => loadData(e)}/>
              {data && data.length !== 0 &&
              <div className="results-container">
                  {data.map(item => (
                      <div key={item.id} className="result-item">
                          <p> {item.city} </p>
                      </div>
                  ))}
              </div>}
          </div>
      );
  }
  ```

  The data will be stored as state and the results will only be shown if the data is non-empty. I'll skip over the CSS for this tutorial, you can find it in the [Git Repo](https://github.com/KunalN25/react-debouncing).

  #### Event Handler

  The `loadData()` function fetches our data and stores the response as state.

  ```cs
  const loadData = async (event) => {
  	const value=event.target.value
      if(value === '') {
          setData(null)
          return
      }
      const response=await fetch(`http://localhost:8000/data/${value}`)
      const res=await response.json()
      setData(res)
  }
  ```

  If no value is entered, simply exit the function. Else, make the request to the node server endpoint. This function is called every time the input changes, so we will debounce this function.

  #### Debounce Implementation using a Custom Hook

  We will write the debouncing logic inside a custom hook. The advantage of custom hooks is that you can re-use the same logic throughout your application. It is highly advisable to do so.

  Create a new folder `custom-hooks` and inside it, create a file `useDebounce.js`. As explained before, the `useDebounce()` method should take a function and delay as parameters and return the debounced function.

  ```routeros
  const useDebounce = (func, delay) => {
      let timeout=null

      return (...args) => {
          if(timeout) clearTimeout(timeout)

          timeout=setTimeout(() => {
              func(...args)
          }, delay)
      }
  }

  export default useDebounce
  ```

  Now, inside the app component, call this method once to get `loadDataDebounced()`.

  ```angelscript
  const loadDataDebounced = useDebounce(loadData, 400)

  ```

  We'll use this new method as the event handler for the `input` element.

  ```reasonml
  <input type="text" onChange={(e) => loadDataDebounced(e)}/>

  ```

  #### Output

  Enter a search string inside the `input` element to test our code.

  ![Screenshot-2024-02-09-190240](https://proxy-prod.omnivore-image-cache.app/835x564,sb5zP6lXJrIxE_8ItjnafuRCnKNnHRzFsCN62XGBrLfQ/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-190240.png)

  On-screen output

  ![Screenshot-2024-02-09-191234](https://proxy-prod.omnivore-image-cache.app/1283x452,sHxsFT2-w6tk2KBgte1fwMNck9ai0l4qA7S8k1fzQDew/https://www.freecodecamp.org/news/content/images/2024/02/Screenshot-2024-02-09-191234.png)

  As you can see in the Network tab, only one request is getting sent instead of three. This makes the search performance much better.

  ## Conclusion

  In this tutorial, you learned what debouncing is and how it is implemented. Debouncing delays the function execution by a certain time and resets the previous timer if the function is called again. 

  Debouncing uses the important concept of closures. I took a slight detour from the implementation to explain what closure is. It can be a confusing concept for beginners, so take your time understanding it. Closures allow you to work with local variables even after a function has finished execution. 

  After that, I showed you a popular use case of debouncing, the auto-complete functionality. The performance of the feature can be improved with debouncing. I also showed you how to implement auto-complete in React and use debouncing with custom hooks. I hope this helps you in future projects.

  If you are unable to understand the content or find the explanation unsatisfactory, let me know. New ideas are always appreciated! Feel free to connect with me on Twitter. Till then, Goodbye!

  ---

  ---

   Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. [Get started](https://www.freecodecamp.org/learn/) 

metadata:
  {"date_added":"2024-02-12 20:23:30","id":"d529f41a-ca28-11ee-97f3-f78c291f6623","status":"ARCHIVED","tags":["read-later","RSS"],"title":"Debouncing in JavaScript – Explained by Building Auto-Complete Functionality in React\n","url_omnivore":"https://omnivore.app/me/debouncing-in-java-script-explained-by-building-auto-complete-fu-18da0bc7510\n","url_original":"https://www.freecodecamp.org/news/deboucing-in-react-autocomplete-example/\n"}


path: Omnivore/2024-02-15 - Configuring Zsh Without Dependencies.md
sortable_path: Omnivore2024-02-15 - Configuring Zsh Without Dependencies.md
title: Configuring Zsh Without Dependencies
lead: # Configuring Zsh Without Dependencies
checksum: 63ca256695889179bb80d04cc17d0ee8b7462a5cc032be91a83c6f7c13f12493
created: 2024-09-15 00:40:57.883278701+00:00
modified: 2024-09-22 22:40:13.395511052+00:00
body:
  # Configuring Zsh Without Dependencies

  ## Highlights

  Zsh read these files in the following order:

  1. `.zshenv` \- Should only contain user’s environment variables.
  2. `.zprofile` \- Can be used to execute commands just after logging in.
  3. `.zshrc` \- Should be used for the shell configuration and for executing commands.
  4. `.zlogin` \- Same purpose than `.zprofile`, but read just after `.zshrc`.
  5. `.zlogout` \- Can be used to execute commands when a shell exit. 

  [source](https://omnivore.app/me/https-thevaluable-dev-zsh-install-configure-mouseless-18dacdbdb29#debc2c0b-4a8b-4073-8a0b-fbddbf99bdcb) 

  ---

  ## Original

  <DIV id="readability-content"><DIV data-omnivore-anchor-idx="1" class="page" id="readability-page-1"><div data-omnivore-anchor-idx="2"><main data-omnivore-anchor-idx="3" role="main"><article data-omnivore-anchor-idx="4"><header data-omnivore-anchor-idx="5"></header><section data-omnivore-anchor-idx="6"><picture data-omnivore-anchor-idx="7"><source data-omnivore-anchor-idx="8" srcset="https://proxy-prod.omnivore-image-cache.app/0x0,srshUvfS3G_ezR33RSwvxWA169d3kznE6QwTzqZ8P_6Y/https://thevaluable.dev/images/2020/zsh/zsh.webp," type="image/webp"><img data-omnivore-anchor-idx="9" data-omnivore-original-src="https://thevaluable.dev/images/2020/zsh/zsh.jpg" width="780" height="520" src="https://proxy-prod.omnivore-image-cache.app/780x520,sRAkOrK0pbDyfGGP8JZyhZzzaifzNMRVFQJs9ceY2rMQ/https://thevaluable.dev/images/2020/zsh/zsh.jpg" alt="Huey, Dewey, and Louie with a Z, S, and H t-shirt"></picture><p data-omnivore-anchor-idx="10">This article is part of a series about Zsh:</p><p data-omnivore-anchor-idx="11">There are many boring tasks we repeat day after day: creating, copying, moving or searching files, launching again and again the same tools, docker containers, and whatnot.</p><p data-omnivore-anchor-idx="12">For a developer, the shell is a precious asset which can increase your efficiency over time. It will bring powerful tools at your fingertips, and, more importantly, it will allow you to automate many parts of your workflow.</p><p data-omnivore-anchor-idx="13">To leverage these functionalities, you’ll need a powerful and flexible shell. Today, I would like to present your next best friend: the Z shell, or Zsh.</p><p data-omnivore-anchor-idx="14">If you look at the documentation (around 450 pages for the <a data-omnivore-anchor-idx="15" href="http://zsh.sourceforge.net/Doc/zsh_a4.pdf" target="_blank" rel="noopener">PDF version</a>), Zsh can feel daunting. There are so many options available, it can be difficult to come up with a basic configuration you can build upon.</p><p data-omnivore-anchor-idx="16">We’ll build, in this article, a basic Zsh config. I’ll explain the meaning of (almost) everything along the way, including:</p><ul data-omnivore-anchor-idx="17"><li data-omnivore-anchor-idx="18">What’s a Unix shell.</li><li data-omnivore-anchor-idx="19">Why Zsh is a good choice.</li><li data-omnivore-anchor-idx="20">How to install Zsh.</li><li data-omnivore-anchor-idx="21">A brief overview of:<ul data-omnivore-anchor-idx="22"><li data-omnivore-anchor-idx="23">Useful environment variables.</li><li data-omnivore-anchor-idx="24">Aliases.</li><li data-omnivore-anchor-idx="25">The Zsh options.</li><li data-omnivore-anchor-idx="26">The Zsh completion.</li><li data-omnivore-anchor-idx="27">The Zsh prompt.</li><li data-omnivore-anchor-idx="28">The Zsh directory stack.</li></ul></li><li data-omnivore-anchor-idx="29">How to configure Zsh to make it Vim-like.</li><li data-omnivore-anchor-idx="30">How to add external plugins to Zsh.</li><li data-omnivore-anchor-idx="31">External programs you can use to improve your Zsh experience.</li></ul><p data-omnivore-anchor-idx="32">Are your keyboard ready? Are you fingers warm? Did you stretch your arms? Let’s begin, then!</p><h2 data-omnivore-anchor-idx="33" id="brief-unix-shell-overview">Brief Unix Shell Overview</h2><p data-omnivore-anchor-idx="34">A shell <em data-omnivore-anchor-idx="35">interpret</em> command lines. You can type them using a prompt in an <em data-omnivore-anchor-idx="36">interactive shell</em>, or you can run shell scripts using a <em data-omnivore-anchor-idx="37">non-interactive shell</em>.</p><p data-omnivore-anchor-idx="38">The shell run just after you logged in with your user. You can imagine the shell as the layer directly above the kernel of Unix-based operating systems (including Linux). Here’s the charismatic <a data-omnivore-anchor-idx="39" href="https://youtu.be/tc4ROCJYbm0?t=248" target="_blank" rel="noopener">Brian Kernighan explaining it casually with his feet on a table</a>.</p><p data-omnivore-anchor-idx="40">When you use a graphical interface (or GUI), you click around with your mouse to perform tasks. When you use a shell, you use plain text instead.</p><p data-omnivore-anchor-idx="41">If you use a graphical interface (like a windows manager or a desktop environment), you’ll need a <em data-omnivore-anchor-idx="42">terminal emulator</em> to access the shell. In the old days, a <a data-omnivore-anchor-idx="43" href="https://en.wikipedia.org/wiki/Computer_terminal" target="_blank" rel="noopener">terminal was a real device</a>. Nowadays, it’s a program.</p><p data-omnivore-anchor-idx="44">The shell gives you access to many powerful programs. They are called CLIs, or Command Line Interfaces.</p><p data-omnivore-anchor-idx="45">At that point, you might wonder: why using a shell, instead of a graphical interface?</p><ul data-omnivore-anchor-idx="46"><li data-omnivore-anchor-idx="47">It’s difficult to get a graphical interface right, especially if your software has many functionalities. It can be simpler to build a CLI to avoid some complexity.</li><li data-omnivore-anchor-idx="48">CLIs are usually faster.</li><li data-omnivore-anchor-idx="49">A developer deals often with plain text. CLIs are great for that.</li><li data-omnivore-anchor-idx="50">Many shells, like Linux shells, allow you to pipe CLIs together in order to create a powerful transformation flow.</li><li data-omnivore-anchor-idx="51">It’s easier to automate textual commands rather than actions on a graphical interface.</li></ul><blockquote data-omnivore-anchor-idx="52"><p data-omnivore-anchor-idx="53">Play around with your command shell, and you’ll be surprised at how much more productive it makes you.</p></blockquote><p data-omnivore-anchor-idx="54">A shell is the keystone of a Mouseless Development Environment, and the most powerful tool you can use as a developer.</p><h2 data-omnivore-anchor-idx="55" id="bash-vs-zsh">Bash vs Zsh</h2><p data-omnivore-anchor-idx="56">There are other Linux shells available out there, including the famous <a data-omnivore-anchor-idx="57" href="https://en.wikipedia.org/wiki/Bash_%28Unix_shell%29" target="_blank" rel="noopener">Bash</a>. Why using Zsh?</p><ul data-omnivore-anchor-idx="58"><li data-omnivore-anchor-idx="59">The level of flexibility and customization of Zsh is crazy.</li><li data-omnivore-anchor-idx="60">You have access to a powerful completion for your favorite CLIs.</li><li data-omnivore-anchor-idx="61">The Vi mode is golden for every Vim lovers.</li><li data-omnivore-anchor-idx="62">There is an important and active community around Zsh.</li><li data-omnivore-anchor-idx="63">Bash scripts are (mostly) compatible with Zsh.</li></ul><p data-omnivore-anchor-idx="64">Bash is simpler than Zsh, but it has also less functionalities.</p><h2 data-omnivore-anchor-idx="65" id="zsh-without-oh-my-zsh">Zsh Without oh-my-zsh</h2><p data-omnivore-anchor-idx="66">You’ll see many advising you to install a Zsh framework with a crazy number of plugins, options, aliases, all already configured. The famous ones are <a data-omnivore-anchor-idx="67" href="https://ohmyz.sh/" target="_blank" rel="noopener">Oh My Zsh</a> and <a data-omnivore-anchor-idx="68" href="https://github.com/sorin-ionescu/prezto" target="_blank" rel="noopener">prezto</a>.</p><p data-omnivore-anchor-idx="69">I tried this approach for years and I think the drawbacks outweigh the benefits:</p><ul data-omnivore-anchor-idx="70"><li data-omnivore-anchor-idx="71">I have no clue what’s included in these frameworks. When I read their documentations, I can’t possibly remember everything it sets. Therefore, I barely use 10% of the functionalities.</li><li data-omnivore-anchor-idx="72">Zsh has already many functionalities and options, it’s even more daunting to have a framework on top.</li><li data-omnivore-anchor-idx="73">A framework is a big external dependency which brings more complexity. If there is a conflict with my own configuration or a bug, it can take a long time to figure out what’s happening.</li><li data-omnivore-anchor-idx="74">A framework impose rules and way of doing I don’t necessarily want, or need.</li></ul><p data-omnivore-anchor-idx="75">Don’t get me wrong: these frameworks are incredible feats. They can be useful to get some inspiration for your own configuration. But I wouldn’t use them directly.</p><h2 data-omnivore-anchor-idx="76" id="let-the-party-begin">Let The Party Begin</h2><p data-omnivore-anchor-idx="77">We’ll now configure Zsh. If the files or folders I’m speaking about don’t exist, you need to create them.</p><p data-omnivore-anchor-idx="78">This configuration was tested with a Linux based system. I have no idea about macOS, but it should work.</p><h3 data-omnivore-anchor-idx="79" id="installing-zsh">Installing Zsh</h3><p data-omnivore-anchor-idx="80">You can install Zsh like everything else:</p><ul data-omnivore-anchor-idx="81"><li data-omnivore-anchor-idx="82">Debian / Ubuntu: <code data-omnivore-anchor-idx="83" class="hljs language-cmake language-ebnf">sudo apt <span data-omnivore-anchor-idx="84" class="hljs-keyword">install</span> zsh</code></li><li data-omnivore-anchor-idx="85">Red Hat: <code data-omnivore-anchor-idx="86" class="hljs language-cmake language-ebnf">sudo yum <span data-omnivore-anchor-idx="87" class="hljs-keyword">install</span> zsh</code></li><li data-omnivore-anchor-idx="88">Arch Linux: <code data-omnivore-anchor-idx="89" class="hljs language-ebnf language-nginx"><span data-omnivore-anchor-idx="90" class="hljs-attribute">sudo pacman -S zsh</span></code></li><li data-omnivore-anchor-idx="91">macOS (with brew): <code data-omnivore-anchor-idx="92" class="hljs language-mipsasm language-armasm"><span data-omnivore-anchor-idx="93" class="hljs-keyword">brew </span><span data-omnivore-anchor-idx="94" class="hljs-keyword">install </span>zsh</code></li></ul><p data-omnivore-anchor-idx="95">Then, run it in a terminal by typing <code data-omnivore-anchor-idx="96" class="hljs language-ebnf"><span data-omnivore-anchor-idx="97" class="hljs-attribute">zsh</span></code>.</p><h3 data-omnivore-anchor-idx="98" id="zsh-config-files">Zsh Config Files</h3><p data-omnivore-anchor-idx="99">To configure Zsh for your user’s session, you can use the following files:</p><ul data-omnivore-anchor-idx="100"><li data-omnivore-anchor-idx="101"><code data-omnivore-anchor-idx="102" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="103" class="hljs-meta"><span data-omnivore-anchor-idx="104" class="hljs-meta-keyword">$ZDOTDIR</span>/.zshenv</span></code></li><li data-omnivore-anchor-idx="105"><code data-omnivore-anchor-idx="106" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="107" class="hljs-meta"><span data-omnivore-anchor-idx="108" class="hljs-meta-keyword">$ZDOTDIR</span>/.zprofile</span></code></li><li data-omnivore-anchor-idx="109"><code data-omnivore-anchor-idx="110" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="111" class="hljs-meta"><span data-omnivore-anchor-idx="112" class="hljs-meta-keyword">$ZDOTDIR</span>/.zshrc</span></code></li><li data-omnivore-anchor-idx="113"><code data-omnivore-anchor-idx="114" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="115" class="hljs-meta"><span data-omnivore-anchor-idx="116" class="hljs-meta-keyword">$ZDOTDIR</span>/.zlogin</span></code></li><li data-omnivore-anchor-idx="117"><code data-omnivore-anchor-idx="118" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="119" class="hljs-meta"><span data-omnivore-anchor-idx="120" class="hljs-meta-keyword">$ZDOTDIR</span>/.zlogout</span></code></li></ul><p data-omnivore-anchor-idx="121">In case you wonder what <code data-omnivore-anchor-idx="122" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="123" class="hljs-meta"><span data-omnivore-anchor-idx="124" class="hljs-meta-keyword">$ZDOTDIR</span></span></code> stands for, we’ll come back to it soon.</p><p data-omnivore-anchor-idx="125">Zsh read these files in the following order:</p><ol data-omnivore-anchor-idx="126"><li data-omnivore-anchor-idx="127"><code data-omnivore-anchor-idx="128" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="129" class="hljs-title">.zshenv</span></code> - Should only contain user’s environment variables.</li><li data-omnivore-anchor-idx="130"><code data-omnivore-anchor-idx="131" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="132" class="hljs-title">.zprofile</span></code> - Can be used to execute commands just after logging in.</li><li data-omnivore-anchor-idx="133"><code data-omnivore-anchor-idx="134" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="135" class="hljs-title">.zshrc</span></code> - Should be used for the shell configuration and for executing commands.</li><li data-omnivore-anchor-idx="136"><code data-omnivore-anchor-idx="137" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="138" class="hljs-title">.zlogin</span></code> - Same purpose than <code data-omnivore-anchor-idx="139" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="140" class="hljs-title">.zprofile</span></code>, but read just after <code data-omnivore-anchor-idx="141" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="142" class="hljs-title">.zshrc</span></code>.</li><li data-omnivore-anchor-idx="143"><code data-omnivore-anchor-idx="144" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="145" class="hljs-title">.zlogout</span></code> - Can be used to execute commands when a shell exit.</li></ol><p data-omnivore-anchor-idx="146">We’ll use only <code data-omnivore-anchor-idx="147" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="148" class="hljs-title">.zshenv</span></code> and <code data-omnivore-anchor-idx="149" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="150" class="hljs-title">.zshrc</span></code> in this article.</p><h3 data-omnivore-anchor-idx="151" id="zsh-config-path">Zsh Config Path</h3><p data-omnivore-anchor-idx="152">By default, Zsh will try to find the user’s configuration files in the <code data-omnivore-anchor-idx="153" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="154" class="hljs-meta"><span data-omnivore-anchor-idx="155" class="hljs-meta-keyword">$HOME</span></span></code> directory. You can change it by setting the environment variable <code data-omnivore-anchor-idx="156" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="157" class="hljs-meta"><span data-omnivore-anchor-idx="158" class="hljs-meta-keyword">$ZDOTDIR</span></span></code>.</p><p data-omnivore-anchor-idx="159">Personally, I like to have all my configuration files in <code data-omnivore-anchor-idx="160" class="hljs language-gams language-arduino"><span data-omnivore-anchor-idx="161" class="hljs-meta"><span data-omnivore-anchor-idx="162" class="hljs-meta-keyword">$HOME</span>/.config</span></code>. To do so:</p><ol data-omnivore-anchor-idx="163"><li data-omnivore-anchor-idx="164">I set the variable <code data-omnivore-anchor-idx="165" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="166" class="hljs-meta"><span data-omnivore-anchor-idx="167" class="hljs-meta-keyword">$XDG</span>_CONFIG_HOME</span></code> as following: <code data-omnivore-anchor-idx="168" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="169" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="170" class="hljs-attribute">XDG_CONFIG_HOME</span>=<span data-omnivore-anchor-idx="171" class="hljs-string">"<span data-omnivore-anchor-idx="172" class="hljs-variable">$HOME</span>/.config"</span></code>.</li><li data-omnivore-anchor-idx="173">I set the environment variable <code data-omnivore-anchor-idx="174" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="175" class="hljs-meta"><span data-omnivore-anchor-idx="176" class="hljs-meta-keyword">$ZDOTDIR</span></span></code>: <code data-omnivore-anchor-idx="177" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="178" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="179" class="hljs-attribute">ZDOTDIR</span>=<span data-omnivore-anchor-idx="180" class="hljs-string">"<span data-omnivore-anchor-idx="181" class="hljs-variable">$XDG_CONFIG_HOME</span>/zsh"</span></code>.</li><li data-omnivore-anchor-idx="182">I put the file <code data-omnivore-anchor-idx="183" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="184" class="hljs-title">.zshrc</span></code> in the <code data-omnivore-anchor-idx="185" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="186" class="hljs-meta"><span data-omnivore-anchor-idx="187" class="hljs-meta-keyword">$ZDOTDIR</span></span></code> directory.</li></ol><p data-omnivore-anchor-idx="188">Most software will use the path in <code data-omnivore-anchor-idx="189" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="190" class="hljs-meta"><span data-omnivore-anchor-idx="191" class="hljs-meta-keyword">$XDG</span>_CONFIG_HOME</span></code> to install their own config files. As a result, you’ll have a clean <code data-omnivore-anchor-idx="192" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="193" class="hljs-meta"><span data-omnivore-anchor-idx="194" class="hljs-meta-keyword">$HOME</span></span></code> directory.</p><p data-omnivore-anchor-idx="195">Unfortunately, the file <code data-omnivore-anchor-idx="196" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="197" class="hljs-title">.zshenv</span></code> <strong data-omnivore-anchor-idx="198">needs to be in your home directory</strong>. It’s where you’ll set <code data-omnivore-anchor-idx="199" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="200" class="hljs-meta"><span data-omnivore-anchor-idx="201" class="hljs-meta-keyword">$ZDOTDIR</span></span></code>. Then, every file read after <code data-omnivore-anchor-idx="202" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="203" class="hljs-title">.zshenv</span></code> can go into your <code data-omnivore-anchor-idx="204" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="205" class="hljs-meta"><span data-omnivore-anchor-idx="206" class="hljs-meta-keyword">$ZDOTDIR</span></span></code> directory.</p><h2 data-omnivore-anchor-idx="207" id="zsh-basic-config">Zsh Basic Config</h2><h3 data-omnivore-anchor-idx="208" id="environment-variables">Environment Variables</h3><p data-omnivore-anchor-idx="209">As we saw, you can set the environment variables you need for your user’s session in the file <code data-omnivore-anchor-idx="210" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="211" class="hljs-meta"><span data-omnivore-anchor-idx="212" class="hljs-meta-keyword">$HOME</span>/.zshenv</span></code>. This file should only define environment variables.</p><p data-omnivore-anchor-idx="213">For example, you can set up the <a data-omnivore-anchor-idx="214" href="https://wiki.archlinux.org/index.php/XDG_Base_Directory" target="_blank" rel="noopener">XDG Base directory</a> there, as seen above:</p><div data-omnivore-anchor-idx="215"><pre data-omnivore-anchor-idx="216" tabindex="0"><code data-omnivore-anchor-idx="217" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="218" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="219" class="hljs-attribute">XDG_CONFIG_HOME</span>=<span data-omnivore-anchor-idx="220" class="hljs-string">"<span data-omnivore-anchor-idx="221" class="hljs-variable">$HOME</span>/.config"</span>
  <span data-omnivore-anchor-idx="222" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="223" class="hljs-attribute">XDG_DATA_HOME</span>=<span data-omnivore-anchor-idx="224" class="hljs-string">"<span data-omnivore-anchor-idx="225" class="hljs-variable">$XDG_CONFIG_HOME</span>/local/share"</span>
  <span data-omnivore-anchor-idx="226" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="227" class="hljs-attribute">XDG_CACHE_HOME</span>=<span data-omnivore-anchor-idx="228" class="hljs-string">"<span data-omnivore-anchor-idx="229" class="hljs-variable">$XDG_CONFIG_HOME</span>/cache"</span>
  </code></pre></div><p data-omnivore-anchor-idx="230">You can also make sure that any program requiring a text editor use your favorite one:</p><div data-omnivore-anchor-idx="231"><pre data-omnivore-anchor-idx="232" tabindex="0"><code data-omnivore-anchor-idx="233" class="hljs language-routeros language-cpp"><span data-omnivore-anchor-idx="234" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="235" class="hljs-attribute">EDITOR</span>=<span data-omnivore-anchor-idx="236" class="hljs-string">"nvim"</span>
  <span data-omnivore-anchor-idx="237" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="238" class="hljs-attribute">VISUAL</span>=<span data-omnivore-anchor-idx="239" class="hljs-string">"nvim"</span>
  </code></pre></div><p data-omnivore-anchor-idx="240">You can set some Zsh environment variables, too:</p><div data-omnivore-anchor-idx="241"><pre data-omnivore-anchor-idx="242" tabindex="0"><code data-omnivore-anchor-idx="243" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="244" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="245" class="hljs-attribute">ZDOTDIR</span>=<span data-omnivore-anchor-idx="246" class="hljs-string">"<span data-omnivore-anchor-idx="247" class="hljs-variable">$XDG_CONFIG_HOME</span>/zsh"</span>

  <span data-omnivore-anchor-idx="248" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="249" class="hljs-attribute">HISTFILE</span>=<span data-omnivore-anchor-idx="250" class="hljs-string">"<span data-omnivore-anchor-idx="251" class="hljs-variable">$ZDOTDIR</span>/.zhistory"</span>    # History filepath
  <span data-omnivore-anchor-idx="252" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="253" class="hljs-attribute">HISTSIZE</span>=10000                   # Maximum events <span data-omnivore-anchor-idx="254" class="hljs-keyword">for</span> internal history
  <span data-omnivore-anchor-idx="255" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="256" class="hljs-attribute">SAVEHIST</span>=10000                   # Maximum events <span data-omnivore-anchor-idx="257" class="hljs-keyword">in</span> history file
  </code></pre></div><p data-omnivore-anchor-idx="258">I already explained the first line. For the other ones, they will:</p><ul data-omnivore-anchor-idx="259"><li data-omnivore-anchor-idx="260">Store your command line history in the file <code data-omnivore-anchor-idx="261" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="262" class="hljs-title">.zhistory</span></code>.</li><li data-omnivore-anchor-idx="263">Allows you to have a history of 10000 entries maximum.</li></ul><p data-omnivore-anchor-idx="264"><a data-omnivore-anchor-idx="265" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/zshenv" target="_blank" rel="noopener">Here’s my .zshenv file</a>, if you need some inspiration.</p><h3 data-omnivore-anchor-idx="266" id="aliases">Aliases</h3><p data-omnivore-anchor-idx="267">Aliases are crucial to improve your efficiency. For example, I have a bunch of aliases for git I use all the time. It’s always easier to type when it’s shorter:</p><div data-omnivore-anchor-idx="268"><pre data-omnivore-anchor-idx="269" tabindex="0"><code data-omnivore-anchor-idx="270" class="hljs language-vhdl language-monkey"><span data-omnivore-anchor-idx="271" class="hljs-keyword">alias</span> gs=<span data-omnivore-anchor-idx="272" class="hljs-symbol">'git</span> status'
  <span data-omnivore-anchor-idx="273" class="hljs-keyword">alias</span> ga=<span data-omnivore-anchor-idx="274" class="hljs-symbol">'git</span> add'
  <span data-omnivore-anchor-idx="275" class="hljs-keyword">alias</span> gp=<span data-omnivore-anchor-idx="276" class="hljs-symbol">'git</span> push'
  <span data-omnivore-anchor-idx="277" class="hljs-keyword">alias</span> gpo=<span data-omnivore-anchor-idx="278" class="hljs-symbol">'git</span> push origin'
  <span data-omnivore-anchor-idx="279" class="hljs-keyword">alias</span> gtd=<span data-omnivore-anchor-idx="280" class="hljs-symbol">'git</span> tag <span data-omnivore-anchor-idx="281" class="hljs-comment">--delete'</span>
  <span data-omnivore-anchor-idx="282" class="hljs-keyword">alias</span> gtdr=<span data-omnivore-anchor-idx="283" class="hljs-symbol">'git</span> tag <span data-omnivore-anchor-idx="284" class="hljs-comment">--delete origin'</span>
  <span data-omnivore-anchor-idx="285" class="hljs-keyword">alias</span> gr=<span data-omnivore-anchor-idx="286" class="hljs-symbol">'git</span> branch -r'
  <span data-omnivore-anchor-idx="287" class="hljs-keyword">alias</span> gplo=<span data-omnivore-anchor-idx="288" class="hljs-symbol">'git</span> pull origin'
  <span data-omnivore-anchor-idx="289" class="hljs-keyword">alias</span> gb=<span data-omnivore-anchor-idx="290" class="hljs-symbol">'git</span> branch '
  <span data-omnivore-anchor-idx="291" class="hljs-keyword">alias</span> gc=<span data-omnivore-anchor-idx="292" class="hljs-symbol">'git</span> commit'
  <span data-omnivore-anchor-idx="293" class="hljs-keyword">alias</span> gd=<span data-omnivore-anchor-idx="294" class="hljs-symbol">'git</span> diff'
  <span data-omnivore-anchor-idx="295" class="hljs-keyword">alias</span> gco=<span data-omnivore-anchor-idx="296" class="hljs-symbol">'git</span> checkout '
  <span data-omnivore-anchor-idx="297" class="hljs-keyword">alias</span> gl=<span data-omnivore-anchor-idx="298" class="hljs-symbol">'git</span> log'
  <span data-omnivore-anchor-idx="299" class="hljs-keyword">alias</span> gr=<span data-omnivore-anchor-idx="300" class="hljs-symbol">'git</span> remote'
  <span data-omnivore-anchor-idx="301" class="hljs-keyword">alias</span> grs=<span data-omnivore-anchor-idx="302" class="hljs-symbol">'git</span> remote show'
  <span data-omnivore-anchor-idx="303" class="hljs-keyword">alias</span> glo=<span data-omnivore-anchor-idx="304" class="hljs-symbol">'git</span> log <span data-omnivore-anchor-idx="305" class="hljs-comment">--pretty="oneline"'</span>
  <span data-omnivore-anchor-idx="306" class="hljs-keyword">alias</span> glol=<span data-omnivore-anchor-idx="307" class="hljs-symbol">'git</span> log <span data-omnivore-anchor-idx="308" class="hljs-comment">--graph --oneline --decorate'</span>
  </code></pre></div><p data-omnivore-anchor-idx="309">I like to have my aliases in one separate file (called, surprisingly, <code data-omnivore-anchor-idx="310" class="hljs language-ebnf language-maxima"><span data-omnivore-anchor-idx="311" class="hljs-attribute">aliases</span></code>), and I source it in my <code data-omnivore-anchor-idx="312" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="313" class="hljs-title">.zshrc</span></code>:</p><div data-omnivore-anchor-idx="314"><pre data-omnivore-anchor-idx="315" tabindex="0"><code data-omnivore-anchor-idx="316" class="hljs language-gradle language-applescript"><span data-omnivore-anchor-idx="317" class="hljs-keyword">source</span> <span data-omnivore-anchor-idx="318" class="hljs-regexp">/path/</span>to<span data-omnivore-anchor-idx="319" class="hljs-regexp">/my/</span>aliases
  </code></pre></div><p data-omnivore-anchor-idx="320">Here are <a data-omnivore-anchor-idx="321" href="https://github.com/Phantas0s/.dotfiles/blob/master/aliases/aliases" target="_blank" rel="noopener">all my aliases</a>.</p><h3 data-omnivore-anchor-idx="322" id="zsh-options">Zsh Options</h3><p data-omnivore-anchor-idx="323">You can set or unset many <a data-omnivore-anchor-idx="324" href="http://zsh.sourceforge.net/Doc/Release/Options.html" target="_blank" rel="noopener">Zsh options</a> using <code data-omnivore-anchor-idx="325" class="hljs language-bash language-ebnf"><span data-omnivore-anchor-idx="326" class="hljs-built_in">setopt</span></code> or <code data-omnivore-anchor-idx="327" class="hljs language-bash language-ebnf"><span data-omnivore-anchor-idx="328" class="hljs-built_in">unsetopt</span></code>. For example:</p><div data-omnivore-anchor-idx="329"><pre data-omnivore-anchor-idx="330" tabindex="0"><code data-omnivore-anchor-idx="331" class="hljs language-bash language-delphi"><span data-omnivore-anchor-idx="332" class="hljs-built_in">setopt</span> HIST_SAVE_NO_DUPS         <span data-omnivore-anchor-idx="333" class="hljs-comment"># Do not write a duplicate event to the history file.</span>
  <span data-omnivore-anchor-idx="334" class="hljs-built_in">unsetopt</span> HIST_SAVE_NO_DUPS       <span data-omnivore-anchor-idx="335" class="hljs-comment"># Write a duplicate event to the history file</span>
  </code></pre></div><p data-omnivore-anchor-idx="336">You can already do a lot of customization only using these options.</p><h3 data-omnivore-anchor-idx="337" id="zsh-completion-system">Zsh Completion System</h3><p data-omnivore-anchor-idx="338">The completion system of Zsh is one of its bigger strength, compared to other shells.</p><p data-omnivore-anchor-idx="339">To initialize the completion for the current Zsh session, you’ll need to call the function <code data-omnivore-anchor-idx="340" class="hljs language-ebnf"><span data-omnivore-anchor-idx="341" class="hljs-attribute">compinit</span></code>. More precisely, you’ll need to add this in your <code data-omnivore-anchor-idx="342" class="hljs language-ebnf"><span data-omnivore-anchor-idx="343" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="344"><pre data-omnivore-anchor-idx="345" tabindex="0"><code data-omnivore-anchor-idx="346" class="hljs language-nginx language-abnf"><span data-omnivore-anchor-idx="347" class="hljs-attribute">autoload</span> -U compinit; <span data-omnivore-anchor-idx="348" class="hljs-attribute">compinit</span>
  </code></pre></div><p data-omnivore-anchor-idx="349">What does it mean?</p><p data-omnivore-anchor-idx="350">The <code data-omnivore-anchor-idx="351" class="hljs language-angelscript language-bash"><span data-omnivore-anchor-idx="352" class="hljs-built_in">auto</span>load</code> command load a file containing shell commands. To find this file, Zsh will look in the directories of the <em data-omnivore-anchor-idx="353">Zsh file search path</em>, defined in the variable <code data-omnivore-anchor-idx="354" class="hljs language-gams language-arcade"><span data-omnivore-anchor-idx="355" class="hljs-meta"><span data-omnivore-anchor-idx="356" class="hljs-meta-keyword">$fpath</span></span></code>, and search a file called <code data-omnivore-anchor-idx="357" class="hljs language-ebnf"><span data-omnivore-anchor-idx="358" class="hljs-attribute">compinit</span></code>.</p><p data-omnivore-anchor-idx="359">When <code data-omnivore-anchor-idx="360" class="hljs language-ebnf"><span data-omnivore-anchor-idx="361" class="hljs-attribute">compinit</span></code> is found, its content will be loaded as a <em data-omnivore-anchor-idx="362">function</em>. The function name will be the name of the file. You can then call this function like any other shell function.</p><div data-omnivore-anchor-idx="363"><p data-omnivore-anchor-idx="364">What about the semi-colon <code data-omnivore-anchor-idx="365" class="hljs language-abnf language-ini"><span data-omnivore-anchor-idx="366" class="hljs-comment">;</span></code>? It’s just a handy way to separate commands. It’s the same as calling <code data-omnivore-anchor-idx="367" class="hljs language-ebnf"><span data-omnivore-anchor-idx="368" class="hljs-attribute">compinit</span></code> on a new line.</p></div><p data-omnivore-anchor-idx="369">Why using autoload, and not sourcing the file by doing <code data-omnivore-anchor-idx="370" class="hljs language-gradle language-arcade"><span data-omnivore-anchor-idx="371" class="hljs-keyword">source</span> ~<span data-omnivore-anchor-idx="372" class="hljs-regexp">/path/</span>of<span data-omnivore-anchor-idx="373" class="hljs-regexp">/compinit</span></code>?</p><ul data-omnivore-anchor-idx="374"><li data-omnivore-anchor-idx="375">It avoids name conflicts if you have an executable with the same name.</li><li data-omnivore-anchor-idx="376">It doesn’t expand aliases thanks to the <code data-omnivore-anchor-idx="377" class="hljs language-diff language-haml"><span data-omnivore-anchor-idx="378" class="hljs-deletion">-U</span></code> option.</li><li data-omnivore-anchor-idx="379">It will load the function only when it’s needed (lazy-loading). It comes in handy to speed up Zsh startup.</li></ul><p data-omnivore-anchor-idx="380">Then, let’s add the following;</p><div data-omnivore-anchor-idx="381"><pre data-omnivore-anchor-idx="382" tabindex="0"><code data-omnivore-anchor-idx="383" class="hljs language-applescript language-dts">_comp_options+=(globdots) <span data-omnivore-anchor-idx="384" class="hljs-comment"># With hidden files</span>
  source /<span data-omnivore-anchor-idx="385" class="hljs-keyword">my</span>/path/<span data-omnivore-anchor-idx="386" class="hljs-keyword">to</span>/zsh/completion.zsh
  </code></pre></div><p data-omnivore-anchor-idx="387">The first line will complete <a data-omnivore-anchor-idx="388" href="https://wiki.archlinux.org/index.php/Dotfiles" target="_blank" rel="noopener">dotfiles</a>.</p><p data-omnivore-anchor-idx="389">The second line source <a data-omnivore-anchor-idx="390" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/completion.zsh" target="_blank" rel="noopener">this file</a>. It’s my personal config for the Zsh completion. I’ve written an <a data-omnivore-anchor-idx="391" href="https://thevaluable.dev/zsh-completion-guide-examples/">article about that</a> if you’re interested to dive more into the completion system.</p><p data-omnivore-anchor-idx="392">Now, the completion should work:</p><ul data-omnivore-anchor-idx="393"><li data-omnivore-anchor-idx="394">If you type <code data-omnivore-anchor-idx="395" class="hljs language-avrasm language-ebnf"><span data-omnivore-anchor-idx="396" class="hljs-keyword">cp</span></code> and hit the tab key, you’ll see that Zsh will complete the command.</li><li data-omnivore-anchor-idx="397">If you type <code data-omnivore-anchor-idx="398" class="hljs language-avrasm language-nginx"><span data-omnivore-anchor-idx="399" class="hljs-keyword">cp</span> -</code> and hit the tab key, Zsh will display the possible arguments for the command.</li></ul><picture data-omnivore-anchor-idx="400"><source data-omnivore-anchor-idx="401" srcset="https://proxy-prod.omnivore-image-cache.app/0x0,sfiBcmObf97NIeUibtBNabIdkPJ0sEqndbvVxv52zQmk/https://thevaluable.dev/images/2020/zsh/auto_complete.webp," type="image/webp"><img data-omnivore-anchor-idx="402" data-omnivore-original-src="https://thevaluable.dev/images/2020/zsh/auto_complete.png" width="780" height="520" src="https://proxy-prod.omnivore-image-cache.app/780x520,shXcZ-OuRAMljwivxvaLoPqZ9COFjw6mY9KEnyqQBCKQ/https://thevaluable.dev/images/2020/zsh/auto_complete.png" alt="Zsh completion in action"></picture><h3 data-omnivore-anchor-idx="403" id="pimp-my-zsh-prompt">Pimp My Zsh Prompt</h3><p data-omnivore-anchor-idx="404">What would be the shell experience without a nice prompt? Dull. Tasteless. Depressing.</p><p data-omnivore-anchor-idx="405">Let’s be honest here: Zsh default prompt is ugly. We need to change it, before our eyes start crying some blood. My needs are simple:</p><ul data-omnivore-anchor-idx="406"><li data-omnivore-anchor-idx="407">The prompt needs to be on one line. I had display problems with two lines.</li><li data-omnivore-anchor-idx="408">The prompt needs to display some git info when necessary.</li></ul><p data-omnivore-anchor-idx="409">From there, I created <a data-omnivore-anchor-idx="410" href="https://github.com/Phantas0s/purification/blob/master/prompt_purification_setup" target="_blank" rel="noopener">my own prompt</a> from <a data-omnivore-anchor-idx="411" href="https://github.com/therealklanni/purity" target="_blank" rel="noopener">another one</a>. It looks like that:</p><picture data-omnivore-anchor-idx="412"><source data-omnivore-anchor-idx="413" srcset="https://proxy-prod.omnivore-image-cache.app/0x0,sEbG09G1juFPo63j_VjykyvXcIJNnKy9th3zqNYTNLns/https://thevaluable.dev/images/2020/zsh/prompt.webp," type="image/webp"><img data-omnivore-anchor-idx="414" data-omnivore-original-src="https://thevaluable.dev/images/2020/zsh/prompt.png" width="780" height="520" src="https://proxy-prod.omnivore-image-cache.app/780x520,sYO_QmAuKGoSirbH4ZiWqkffidw64lNbq8Ixd6jKGOew/https://thevaluable.dev/images/2020/zsh/prompt.png" alt="Zsh prompt"></picture><p data-omnivore-anchor-idx="415">If you open the prompt script, you’ll see that it’s pretty simple:</p><ul data-omnivore-anchor-idx="416"><li data-omnivore-anchor-idx="417">I set two environment variables: <code data-omnivore-anchor-idx="418" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="419" class="hljs-meta"><span data-omnivore-anchor-idx="420" class="hljs-meta-keyword">$PROMPT</span></span></code> and <code data-omnivore-anchor-idx="421" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="422" class="hljs-meta"><span data-omnivore-anchor-idx="423" class="hljs-meta-keyword">$RPROMPT</span></span></code>. The first one format the left prompt, the second display git information on the far right.</li><li data-omnivore-anchor-idx="424">You can add some formatting styles using, for example, <code data-omnivore-anchor-idx="425" class="hljs language-haml language-cos"><span data-omnivore-anchor-idx="426" class="hljs-tag">%<span data-omnivore-anchor-idx="427" class="hljs-selector-tag">F</span>{blue}</span>%f</code> to change the color, or <code data-omnivore-anchor-idx="428" class="hljs language-cos language-gcode"><span data-omnivore-anchor-idx="429" class="hljs-built_in">%Bmy</span>-cool-prompt<span data-omnivore-anchor-idx="430" class="hljs-built_in">%b</span></code> to make everything bold.</li></ul><p data-omnivore-anchor-idx="431">This prompt doesn’t need any external <a data-omnivore-anchor-idx="432" href="https://thevaluable.dev/cohesion-coupling-guide-examples/">dependency</a>. You can copy it right away and modify it as much as you want.</p><p data-omnivore-anchor-idx="433"><a data-omnivore-anchor-idx="434" href="http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html" target="_blank" rel="noopener">Here’s everything you need, to create the prompt of your dream</a>.</p><p data-omnivore-anchor-idx="435">To load the prompt, you need to add something like that in your <code data-omnivore-anchor-idx="436" class="hljs language-ebnf"><span data-omnivore-anchor-idx="437" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="438"><pre data-omnivore-anchor-idx="439" tabindex="0"><code data-omnivore-anchor-idx="440" class="hljs language-applescript language-arcade">fpath=(/<span data-omnivore-anchor-idx="441" class="hljs-keyword">my</span>/path/<span data-omnivore-anchor-idx="442" class="hljs-keyword">to</span>/zsh/prompt $fpath)
  autoload -Uz name_of_the_prompt_file; name_of_the_prompt_file
  </code></pre></div><p data-omnivore-anchor-idx="443">The first line will add the folder containing the prompt to <code data-omnivore-anchor-idx="444" class="hljs language-gams language-arcade"><span data-omnivore-anchor-idx="445" class="hljs-meta"><span data-omnivore-anchor-idx="446" class="hljs-meta-keyword">$fpath</span></span></code>, as discussed above. It will also ensure that any function declared in the folder <code data-omnivore-anchor-idx="447" class="hljs language-applescript language-awk">/<span data-omnivore-anchor-idx="448" class="hljs-keyword">my</span>/path/<span data-omnivore-anchor-idx="449" class="hljs-keyword">to</span>/zsh/prompt</code> will overwrite every other ones with the same name, in other <code data-omnivore-anchor-idx="450" class="hljs language-ebnf"><span data-omnivore-anchor-idx="451" class="hljs-attribute">fpath</span></code> folders.</p><p data-omnivore-anchor-idx="452">The second line autoload the prompt itself.</p><p data-omnivore-anchor-idx="453">This prompt require <a data-omnivore-anchor-idx="454" href="https://fontawesome.com/v4.7.0/" target="_blank" rel="noopener">font awesome 4</a> for the git icons. You can download the font and install it, or you can change the icons.</p><h3 data-omnivore-anchor-idx="455" id="zsh-directory-stack">Zsh Directory Stack</h3><p data-omnivore-anchor-idx="456">Zsh has commands to <a data-omnivore-anchor-idx="457" href="http://zsh.sourceforge.net/Intro/intro_6.html" target="_blank" rel="noopener">push and pop directories on a directory stack</a>.</p><p data-omnivore-anchor-idx="458">By manipulating this stack, you can set up an history of directory visited, and be able to jump back to these directories.</p><p data-omnivore-anchor-idx="459">First, let’s set some options in your <code data-omnivore-anchor-idx="460" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="461" class="hljs-title">.zshrc</span></code>:</p><div data-omnivore-anchor-idx="462"><pre data-omnivore-anchor-idx="463" tabindex="0"><code data-omnivore-anchor-idx="464" class="hljs language-bash language-nginx"><span data-omnivore-anchor-idx="465" class="hljs-built_in">setopt</span> AUTO_PUSHD           <span data-omnivore-anchor-idx="466" class="hljs-comment"># Push the current directory visited on the stack.</span>
  <span data-omnivore-anchor-idx="467" class="hljs-built_in">setopt</span> PUSHD_IGNORE_DUPS    <span data-omnivore-anchor-idx="468" class="hljs-comment"># Do not store duplicates in the stack.</span>
  <span data-omnivore-anchor-idx="469" class="hljs-built_in">setopt</span> PUSHD_SILENT         <span data-omnivore-anchor-idx="470" class="hljs-comment"># Do not print the directory stack after pushd or popd.</span>
  </code></pre></div><p data-omnivore-anchor-idx="471">Then, you can create these aliases:</p><div data-omnivore-anchor-idx="472"><pre data-omnivore-anchor-idx="473" tabindex="0"><code data-omnivore-anchor-idx="474" class="hljs language-bash language-perl"><span data-omnivore-anchor-idx="475" class="hljs-built_in">alias</span> d=<span data-omnivore-anchor-idx="476" class="hljs-string">'dirs -v'</span>
  <span data-omnivore-anchor-idx="477" class="hljs-keyword">for</span> index ({1..9}) <span data-omnivore-anchor-idx="478" class="hljs-built_in">alias</span> <span data-omnivore-anchor-idx="479" class="hljs-string">"<span data-omnivore-anchor-idx="480" class="hljs-variable">$index</span>"</span>=<span data-omnivore-anchor-idx="481" class="hljs-string">"cd +<span data-omnivore-anchor-idx="482" class="hljs-variable">${index}</span>"</span>; <span data-omnivore-anchor-idx="483" class="hljs-built_in">unset</span> index
  </code></pre></div><p data-omnivore-anchor-idx="484">What does it do?</p><ul data-omnivore-anchor-idx="485"><li data-omnivore-anchor-idx="486">Every directory visited will populate the stack.</li><li data-omnivore-anchor-idx="487">When you use the alias <code data-omnivore-anchor-idx="488" class="hljs language-ebnf"><span data-omnivore-anchor-idx="489" class="hljs-attribute">d</span></code>, it will display the directories on the stack prefixed with a number.</li><li data-omnivore-anchor-idx="490">The line <code data-omnivore-anchor-idx="491" class="hljs language-perl language-bash"><span data-omnivore-anchor-idx="492" class="hljs-keyword">for</span> <span data-omnivore-anchor-idx="493" class="hljs-keyword">index</span> ({<span data-omnivore-anchor-idx="494" class="hljs-number">1</span>..<span data-omnivore-anchor-idx="495" class="hljs-number">9</span>}) alias <span data-omnivore-anchor-idx="496" class="hljs-string">"$index"</span>=<span data-omnivore-anchor-idx="497" class="hljs-string">"cd +<span data-omnivore-anchor-idx="498" class="hljs-subst">${<span data-omnivore-anchor-idx="499" class="hljs-keyword">index</span>}</span>"</span>; unset <span data-omnivore-anchor-idx="500" class="hljs-keyword">index</span></code> will create aliases from 1 to 9. They will allow you to jump directly in whatever directory on your stack.</li></ul><p data-omnivore-anchor-idx="501">For example, if you execute <code data-omnivore-anchor-idx="502" class="hljs language-angelscript language-lsl"><span data-omnivore-anchor-idx="503" class="hljs-number">1</span></code> in Zsh, you’ll jump to the directory prefixed with <code data-omnivore-anchor-idx="504" class="hljs language-angelscript language-lsl"><span data-omnivore-anchor-idx="505" class="hljs-number">1</span></code> in your stack list.</p><p data-omnivore-anchor-idx="506">You can also increase <code data-omnivore-anchor-idx="507" class="hljs language-angelscript language-lsl">index ({<span data-omnivore-anchor-idx="508" class="hljs-number">1.</span><span data-omnivore-anchor-idx="509" class="hljs-number">.9</span>})</code> to <code data-omnivore-anchor-idx="510" class="hljs language-angelscript language-lsl">index ({<span data-omnivore-anchor-idx="511" class="hljs-number">1.</span><span data-omnivore-anchor-idx="512" class="hljs-number">.100</span>})</code> for example, if you want to be able to jump back to 100 directories.</p><p data-omnivore-anchor-idx="513">For example, you can do that:</p><div data-omnivore-anchor-idx="514"><pre data-omnivore-anchor-idx="515" tabindex="0"><code data-omnivore-anchor-idx="516" class="hljs language-jboss-cli language-arduino">~ &gt; <span data-omnivore-anchor-idx="517" class="hljs-keyword">cd</span> <span data-omnivore-anchor-idx="518" class="hljs-string">.config</span>
  ~<span data-omnivore-anchor-idx="519" class="hljs-string">/.config</span> &gt; <span data-omnivore-anchor-idx="520" class="hljs-keyword">cd</span> devdash
  ~<span data-omnivore-anchor-idx="521" class="hljs-string">/.config/devdash</span> &gt; <span data-omnivore-anchor-idx="522" class="hljs-keyword">cd</span> <span data-omnivore-anchor-idx="523" class="hljs-string">..</span>
  ~<span data-omnivore-anchor-idx="524" class="hljs-string">/.config</span> &gt; <span data-omnivore-anchor-idx="525" class="hljs-keyword">cd</span> i3
  ~<span data-omnivore-anchor-idx="526" class="hljs-string">/.config/i3</span> &gt; <span data-omnivore-anchor-idx="527" class="hljs-keyword">cd</span> <span data-omnivore-anchor-idx="528" class="hljs-string">..</span>
  ~<span data-omnivore-anchor-idx="529" class="hljs-string">/.config</span> &gt; d
  0       ~<span data-omnivore-anchor-idx="530" class="hljs-string">/.config</span>
  1       ~<span data-omnivore-anchor-idx="531" class="hljs-string">/.config/i3</span>
  2       ~<span data-omnivore-anchor-idx="532" class="hljs-string">/.config/devdash</span>
  3       ~
  ~<span data-omnivore-anchor-idx="533" class="hljs-string">/.config</span> &gt; 2
  ~<span data-omnivore-anchor-idx="534" class="hljs-string">/.config/devdash</span> &gt; 
  </code></pre></div><h3 data-omnivore-anchor-idx="535" id="zsh-by-default">Zsh By Default</h3><p data-omnivore-anchor-idx="536">When you’re ready psychologically to set Zsh as your default shell, you can run these commands:</p><ul data-omnivore-anchor-idx="537"><li data-omnivore-anchor-idx="538">For Linux: <code data-omnivore-anchor-idx="539" class="hljs language-reasonml language-arcade">chsh -s <span data-omnivore-anchor-idx="540" class="hljs-constructor">$(<span data-omnivore-anchor-idx="541" class="hljs-params">which</span> <span data-omnivore-anchor-idx="542" class="hljs-params">zsh</span>)</span></code></li><li data-omnivore-anchor-idx="543">For macOS: <code data-omnivore-anchor-idx="544" class="hljs language-reasonml language-bash">sudo sh -c <span data-omnivore-anchor-idx="545" class="hljs-string">"echo $(which zsh) &gt;&gt; /etc/shells"</span><span data-omnivore-anchor-idx="546" class="hljs-operator"> &amp;&amp; </span>chsh -s <span data-omnivore-anchor-idx="547" class="hljs-constructor">$(<span data-omnivore-anchor-idx="548" class="hljs-params">which</span> <span data-omnivore-anchor-idx="549" class="hljs-params">zsh</span>)</span></code></li></ul><p data-omnivore-anchor-idx="550">A good soul on Reddit whispered me that Zsh is now the default shell from macOS Catalina onwards, so you don’t necessarily need the above command.</p><p data-omnivore-anchor-idx="551">Zsh is now part of your life. Congratulation!</p><h2 data-omnivore-anchor-idx="552" id="zsh-with-vim-flavors">Zsh With Vim Flavors</h2><p data-omnivore-anchor-idx="553">For editing purposes, <a data-omnivore-anchor-idx="554" href="https://thevaluable.dev/phpstorm-vs-vim/">Vim is my best friend</a>. I love when CLIs use some Vim key binding, and Zsh gives you even more than that. If you’d like to learn Vim, this <a data-omnivore-anchor-idx="555" href="https://thevaluable.dev/vim-commands-beginner/" target="_blank" rel="noopener">series of articles</a> can help.</p><h3 data-omnivore-anchor-idx="556" id="activating-vi-mode">Activating Vi Mode</h3><p data-omnivore-anchor-idx="557">Zsh has a Vi mode you can enable by adding the following in your <code data-omnivore-anchor-idx="558" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="559" class="hljs-title">.zshrc</span></code>:</p><div data-omnivore-anchor-idx="560"><pre data-omnivore-anchor-idx="561" tabindex="0"><code data-omnivore-anchor-idx="562" class="hljs language-routeros language-bash">bindkey -v
  <span data-omnivore-anchor-idx="563" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="564" class="hljs-attribute">KEYTIMEOUT</span>=1
  </code></pre></div><p data-omnivore-anchor-idx="565">You can now switch between INSERT and NORMAL mode (called also COMMAND mode) with the <code data-omnivore-anchor-idx="566" class="hljs language-ebnf"><span data-omnivore-anchor-idx="567" class="hljs-attribute">ESC</span></code> key, and use the familiar Vim keystrokes to edit what you’re typing in your shell prompt. I write the different modes in uppercase here for clarity, but it doesn’t have to be.</p><p data-omnivore-anchor-idx="568">The second line <code data-omnivore-anchor-idx="569" class="hljs language-routeros language-angelscript"><span data-omnivore-anchor-idx="570" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="571" class="hljs-attribute">KEYTIMEOUT</span>=1</code> makes the switch between modes quicker.</p><h3 data-omnivore-anchor-idx="572" id="changing-cursor">Changing Cursor</h3><p data-omnivore-anchor-idx="573">A visual indicator to show the current mode (NORMAL or INSERT) could be nice. In Vim, my cursor is a beam <code data-omnivore-anchor-idx="574" class="hljs language-1c"><span data-omnivore-anchor-idx="575" class="hljs-string">|</span></code> when I’m in INSERT mode, and a block <code data-omnivore-anchor-idx="576" class="hljs language-undefined">█</code> when I’m in NORMAL mode. I wanted the same for Zsh.</p><p data-omnivore-anchor-idx="577">You can add the following in your <code data-omnivore-anchor-idx="578" class="hljs language-ebnf"><span data-omnivore-anchor-idx="579" class="hljs-attribute">zshrc</span></code>, or autoload it from a file, <a data-omnivore-anchor-idx="580" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/plugins/cursor_mode" target="_blank" rel="noopener">as I did</a>.</p><div data-omnivore-anchor-idx="581"><pre data-omnivore-anchor-idx="582" tabindex="0"><code data-omnivore-anchor-idx="583" class="hljs language-bash language-perl"><span data-omnivore-anchor-idx="584" class="hljs-function"><span data-omnivore-anchor-idx="585" class="hljs-title">cursor_mode</span></span>() {
      <span data-omnivore-anchor-idx="586" class="hljs-comment"># See https://ttssh2.osdn.jp/manual/4/en/usage/tips/vim.html for cursor shapes</span>
      cursor_block=<span data-omnivore-anchor-idx="587" class="hljs-string">'\e[2 q'</span>
      cursor_beam=<span data-omnivore-anchor-idx="588" class="hljs-string">'\e[6 q'</span>

      <span data-omnivore-anchor-idx="589" class="hljs-keyword">function</span> <span data-omnivore-anchor-idx="590" class="hljs-built_in">zle</span>-keymap-select {
          <span data-omnivore-anchor-idx="591" class="hljs-keyword">if</span> [[ <span data-omnivore-anchor-idx="592" class="hljs-variable">${KEYMAP}</span> == vicmd ]] ||
              [[ <span data-omnivore-anchor-idx="593" class="hljs-variable">$1</span> = <span data-omnivore-anchor-idx="594" class="hljs-string">'block'</span> ]]; <span data-omnivore-anchor-idx="595" class="hljs-keyword">then</span>
              <span data-omnivore-anchor-idx="596" class="hljs-built_in">echo</span> -ne <span data-omnivore-anchor-idx="597" class="hljs-variable">$cursor_block</span>
          <span data-omnivore-anchor-idx="598" class="hljs-keyword">elif</span> [[ <span data-omnivore-anchor-idx="599" class="hljs-variable">${KEYMAP}</span> == main ]] ||
              [[ <span data-omnivore-anchor-idx="600" class="hljs-variable">${KEYMAP}</span> == viins ]] ||
              [[ <span data-omnivore-anchor-idx="601" class="hljs-variable">${KEYMAP}</span> = <span data-omnivore-anchor-idx="602" class="hljs-string">''</span> ]] ||
              [[ <span data-omnivore-anchor-idx="603" class="hljs-variable">$1</span> = <span data-omnivore-anchor-idx="604" class="hljs-string">'beam'</span> ]]; <span data-omnivore-anchor-idx="605" class="hljs-keyword">then</span>
              <span data-omnivore-anchor-idx="606" class="hljs-built_in">echo</span> -ne <span data-omnivore-anchor-idx="607" class="hljs-variable">$cursor_beam</span>
          <span data-omnivore-anchor-idx="608" class="hljs-keyword">fi</span>
      }

      <span data-omnivore-anchor-idx="609" class="hljs-built_in">zle</span>-line-<span data-omnivore-anchor-idx="610" class="hljs-function"><span data-omnivore-anchor-idx="611" class="hljs-title">init</span></span>() {
          <span data-omnivore-anchor-idx="612" class="hljs-built_in">echo</span> -ne <span data-omnivore-anchor-idx="613" class="hljs-variable">$cursor_beam</span>
      }

      <span data-omnivore-anchor-idx="614" class="hljs-built_in">zle</span> -N <span data-omnivore-anchor-idx="615" class="hljs-built_in">zle</span>-keymap-select
      <span data-omnivore-anchor-idx="616" class="hljs-built_in">zle</span> -N <span data-omnivore-anchor-idx="617" class="hljs-built_in">zle</span>-line-init
  }

  cursor_mode
  </code></pre></div><p data-omnivore-anchor-idx="618">You can now speak about beams and blocks with passion and verve.</p><h3 data-omnivore-anchor-idx="619" id="vim-mapping-for-completion">Vim Mapping For Completion</h3><p data-omnivore-anchor-idx="620">To give Zsh more of a Vim taste, we can set up the keys <code data-omnivore-anchor-idx="621" class="hljs language-ebnf"><span data-omnivore-anchor-idx="622" class="hljs-attribute">hjkl</span></code> to navigate the completion menu.</p><p data-omnivore-anchor-idx="623">First, add the following to your <code data-omnivore-anchor-idx="624" class="hljs language-ebnf"><span data-omnivore-anchor-idx="625" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="626"><pre data-omnivore-anchor-idx="627" tabindex="0"><code data-omnivore-anchor-idx="628" class="hljs language-vim language-bash">zmodload zsh/complist
  bindkey -M menuselect <span data-omnivore-anchor-idx="629" class="hljs-string">'h'</span> <span data-omnivore-anchor-idx="630" class="hljs-keyword">vi</span>-backward-char
  bindkey -M menuselect <span data-omnivore-anchor-idx="631" class="hljs-string">'k'</span> <span data-omnivore-anchor-idx="632" class="hljs-keyword">vi</span>-<span data-omnivore-anchor-idx="633" class="hljs-keyword">up</span>-<span data-omnivore-anchor-idx="634" class="hljs-built_in">line</span>-<span data-omnivore-anchor-idx="635" class="hljs-built_in">or</span>-<span data-omnivore-anchor-idx="636" class="hljs-keyword">history</span>
  bindkey -M menuselect <span data-omnivore-anchor-idx="637" class="hljs-string">'l'</span> <span data-omnivore-anchor-idx="638" class="hljs-keyword">vi</span>-forward-char
  bindkey -M menuselect <span data-omnivore-anchor-idx="639" class="hljs-string">'j'</span> <span data-omnivore-anchor-idx="640" class="hljs-keyword">vi</span>-down-<span data-omnivore-anchor-idx="641" class="hljs-built_in">line</span>-<span data-omnivore-anchor-idx="642" class="hljs-built_in">or</span>-<span data-omnivore-anchor-idx="643" class="hljs-keyword">history</span>
  </code></pre></div><p data-omnivore-anchor-idx="644">We load here the Zsh module <code data-omnivore-anchor-idx="645" class="hljs language-ebnf"><span data-omnivore-anchor-idx="646" class="hljs-attribute">complist</span></code>. Modules have functionalities which are not part of the Zsh’s core, but they can be loaded on demand. <a data-omnivore-anchor-idx="647" href="http://zsh.sourceforge.net/Doc/Release/Zsh-Modules.html" target="_blank" rel="noopener">Many different modules are available</a> for your needs.</p><p data-omnivore-anchor-idx="648">Here, the module <code data-omnivore-anchor-idx="649" class="hljs language-ebnf"><span data-omnivore-anchor-idx="650" class="hljs-attribute">complist</span></code> give you access to the keymap <code data-omnivore-anchor-idx="651" class="hljs language-ebnf"><span data-omnivore-anchor-idx="652" class="hljs-attribute">menuselect</span></code>, to customize the menu selection during completion, including how to select what you want.</p><p data-omnivore-anchor-idx="653">In general, the command <code data-omnivore-anchor-idx="654" class="hljs language-armasm language-bash"><span data-omnivore-anchor-idx="655" class="hljs-keyword">bindkey </span>-M</code> bind a key to a specific <em data-omnivore-anchor-idx="656">keymap</em>. A keymap is a set of keystrokes bind to specific Zsh functions. In this case, the keymap <code data-omnivore-anchor-idx="657" class="hljs language-ebnf"><span data-omnivore-anchor-idx="658" class="hljs-attribute">menuselect</span></code> bind keystrokes with selecting something in a list.</p><p data-omnivore-anchor-idx="659">To list all the keymaps available (depending on the modules you’ve loaded), you can run in your shell <code data-omnivore-anchor-idx="660" class="hljs language-armasm language-bash"><span data-omnivore-anchor-idx="661" class="hljs-keyword">bindkey </span>-l</code> (for <code data-omnivore-anchor-idx="662" class="hljs language-ebnf"><span data-omnivore-anchor-idx="663" class="hljs-attribute">l</span></code>ist). You can also <a data-omnivore-anchor-idx="664" href="http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Keymaps" target="_blank" rel="noopener">find the default ones here</a>.</p><p data-omnivore-anchor-idx="665">Last thing: you should always load the module <code data-omnivore-anchor-idx="666" class="hljs language-undefined">zsh/complist</code> <em data-omnivore-anchor-idx="667">before</em> autoloading <code data-omnivore-anchor-idx="668" class="hljs language-ebnf"><span data-omnivore-anchor-idx="669" class="hljs-attribute">compinit</span></code>.</p><h3 data-omnivore-anchor-idx="670" id="editing-command-lines-in-vim">Editing Command Lines In Vim</h3><p data-omnivore-anchor-idx="671">Good news: you can use your favorite editor to edit the commands you’re typing in your prompt! Let’s add these lines in your <code data-omnivore-anchor-idx="672" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="673" class="hljs-title">.zshrc</span></code> to do so:</p><div data-omnivore-anchor-idx="674"><pre data-omnivore-anchor-idx="675" tabindex="0"><code data-omnivore-anchor-idx="676" class="hljs language-vim language-gauss">autoload -Uz <span data-omnivore-anchor-idx="677" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="678" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="679" class="hljs-built_in">line</span>
  zle -<span data-omnivore-anchor-idx="680" class="hljs-keyword">N</span> <span data-omnivore-anchor-idx="681" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="682" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="683" class="hljs-built_in">line</span>
  bindkey -M vicmd v <span data-omnivore-anchor-idx="684" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="685" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="686" class="hljs-built_in">line</span>
  </code></pre></div><p data-omnivore-anchor-idx="687">Here, we autoload <code data-omnivore-anchor-idx="688" class="hljs language-vim language-gauss"><span data-omnivore-anchor-idx="689" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="690" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="691" class="hljs-built_in">line</span></code>, a function from the module <a data-omnivore-anchor-idx="692" href="https://linux.die.net/man/1/zshcontrib" target="_blank" rel="noopener">zshcontrib</a>, which includes many contributions from Zsh users. This specific function let you edit a command line in your visual editor, defined by the environment variable <code data-omnivore-anchor-idx="693" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="694" class="hljs-meta"><span data-omnivore-anchor-idx="695" class="hljs-meta-keyword">$VISUAL</span></span></code> (or <code data-omnivore-anchor-idx="696" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="697" class="hljs-meta"><span data-omnivore-anchor-idx="698" class="hljs-meta-keyword">$EDITOR</span></span></code>). Great! That’s what we wanted.</p><p data-omnivore-anchor-idx="699">We already saw <code data-omnivore-anchor-idx="700" class="hljs language-armasm language-bash"><span data-omnivore-anchor-idx="701" class="hljs-keyword">bindkey </span>-M</code>. Using the keymap <code data-omnivore-anchor-idx="702" class="hljs language-ebnf"><span data-omnivore-anchor-idx="703" class="hljs-attribute">vicmd</span></code>, we can bind commands to some NORMAL mode keystrokes. It means that, when you’re in NORMAL mode, you can hit <code data-omnivore-anchor-idx="704" class="hljs language-ebnf"><span data-omnivore-anchor-idx="705" class="hljs-attribute">v</span></code> to directly edit your command in your editor.</p><h3 data-omnivore-anchor-idx="706" id="adding-text-objects">Adding Text Objects</h3><p data-omnivore-anchor-idx="707">If you use the Vi-mode of Zsh for a while, you’ll notice that there are no text objects for quotes or brackets: impossible to do something like <code data-omnivore-anchor-idx="708" class="hljs language-1c language-vim">da<span data-omnivore-anchor-idx="709" class="hljs-string">"</span></code> (to delete a quoted substring) or <code data-omnivore-anchor-idx="710" class="hljs language-isbl language-stata"><span data-omnivore-anchor-idx="711" class="hljs-function"><span data-omnivore-anchor-idx="712" class="hljs-title">ci</span>(</span></code> (to change inside parenthesis). Zsh supports these, you just need to generate and bind them to specific Zsh widgets:</p><div data-omnivore-anchor-idx="713"><pre data-omnivore-anchor-idx="714" tabindex="0"><code data-omnivore-anchor-idx="715" class="hljs language-bash language-properties"><span data-omnivore-anchor-idx="716" class="hljs-built_in">autoload</span> -Uz select-bracketed select-quoted
  <span data-omnivore-anchor-idx="717" class="hljs-built_in">zle</span> -N select-quoted
  <span data-omnivore-anchor-idx="718" class="hljs-built_in">zle</span> -N select-bracketed
  <span data-omnivore-anchor-idx="719" class="hljs-keyword">for</span> km <span data-omnivore-anchor-idx="720" class="hljs-keyword">in</span> viopp visual; <span data-omnivore-anchor-idx="721" class="hljs-keyword">do</span>
    <span data-omnivore-anchor-idx="722" class="hljs-built_in">bindkey</span> -M <span data-omnivore-anchor-idx="723" class="hljs-variable">$km</span> -- <span data-omnivore-anchor-idx="724" class="hljs-string">'-'</span> vi-up-line-or-history
    <span data-omnivore-anchor-idx="725" class="hljs-keyword">for</span> c <span data-omnivore-anchor-idx="726" class="hljs-keyword">in</span> {a,i}<span data-omnivore-anchor-idx="727" class="hljs-variable">${(s..)^:-\'\"\`\|,./:;=+@}</span>; <span data-omnivore-anchor-idx="728" class="hljs-keyword">do</span>
      <span data-omnivore-anchor-idx="729" class="hljs-built_in">bindkey</span> -M <span data-omnivore-anchor-idx="730" class="hljs-variable">$km</span> <span data-omnivore-anchor-idx="731" class="hljs-variable">$c</span> select-quoted
    <span data-omnivore-anchor-idx="732" class="hljs-keyword">done</span>
    <span data-omnivore-anchor-idx="733" class="hljs-keyword">for</span> c <span data-omnivore-anchor-idx="734" class="hljs-keyword">in</span> {a,i}<span data-omnivore-anchor-idx="735" class="hljs-variable">${(s..)^:-'()[]{}</span>&lt;&gt;bB<span data-omnivore-anchor-idx="736" class="hljs-string">'}; do
      bindkey -M $km $c select-bracketed
    done
  done
  </span></code></pre></div><p data-omnivore-anchor-idx="737">If you want to know more about Zsh widgets, I’ve <a data-omnivore-anchor-idx="738" href="https://thevaluable.dev/zsh-line-editor-configuration-mouseless/">written another article about that</a>, where I also explain the code above.</p><h3 data-omnivore-anchor-idx="739" id="surrounding">Surrounding</h3><p data-omnivore-anchor-idx="740">Zsh also allows us to mimic the famous <a data-omnivore-anchor-idx="741" href="https://github.com/tpope/vim-surround" target="_blank" rel="noopener">Tim Pope’s surround plugin</a>. Just add the following to your <code data-omnivore-anchor-idx="742" class="hljs language-ebnf"><span data-omnivore-anchor-idx="743" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="744"><pre data-omnivore-anchor-idx="745" tabindex="0"><code data-omnivore-anchor-idx="746" class="hljs language-smali language-dsconfig">autoload -Uz surround
  zle -N delete-surround surround
  zle -N<span data-omnivore-anchor-idx="747" class="hljs-built_in"> add-surround </span>surround
  zle -N change-surround surround
  bindkey -M vicmd cs change-surround
  bindkey -M vicmd ds delete-surround
  bindkey -M vicmd ys<span data-omnivore-anchor-idx="748" class="hljs-built_in"> add-surround
  </span>bindkey -M visual S<span data-omnivore-anchor-idx="749" class="hljs-built_in"> add-surround
  </span></code></pre></div><p data-omnivore-anchor-idx="750">You can then use <code data-omnivore-anchor-idx="751" class="hljs language-ebnf language-stata"><span data-omnivore-anchor-idx="752" class="hljs-attribute">cs</span></code> (change surrounding), <code data-omnivore-anchor-idx="753" class="hljs language-ebnf language-stata"><span data-omnivore-anchor-idx="754" class="hljs-attribute">ds</span></code> (delete surrounding), <code data-omnivore-anchor-idx="755" class="hljs language-ebnf"><span data-omnivore-anchor-idx="756" class="hljs-attribute">ys</span></code> (add surrounding) in Zsh’s NORMAL mode.</p><h2 data-omnivore-anchor-idx="757" id="zsh-plugins">Zsh Plugins</h2><p data-omnivore-anchor-idx="758">The term “plugin”, as I use it, has nothing official. People often speak about Zsh plugins as external pieces of configuration you can add to your own.</p><p data-omnivore-anchor-idx="759">There are many of these plugins available for Zsh. Many of them are part of Zsh frameworks.</p><h3 data-omnivore-anchor-idx="760" id="zsh-completions">Zsh Completions</h3><p data-omnivore-anchor-idx="761">By default, Zsh can complete already many popular CLIs like <code data-omnivore-anchor-idx="762" class="hljs language-bash language-dos"><span data-omnivore-anchor-idx="763" class="hljs-built_in">cd</span></code>, <code data-omnivore-anchor-idx="764" class="hljs language-avrasm language-ebnf"><span data-omnivore-anchor-idx="765" class="hljs-keyword">cp</span></code>, <code data-omnivore-anchor-idx="766" class="hljs language-ebnf"><span data-omnivore-anchor-idx="767" class="hljs-attribute">git</span></code>, and so on.</p><p data-omnivore-anchor-idx="768">The plugin <a data-omnivore-anchor-idx="769" href="https://github.com/zsh-users/zsh-completions" target="_blank" rel="noopener">zsh-completions</a> add even more completions. The <a data-omnivore-anchor-idx="770" href="https://github.com/zsh-users/zsh-completions/tree/master/src" target="_blank" rel="noopener">list of the newly supported CLIs is here</a></p><p data-omnivore-anchor-idx="771">If you don’t use any of the program listed, you don’t need this plugin.</p><p data-omnivore-anchor-idx="772">I added <code data-omnivore-anchor-idx="773" class="hljs language-ebnf"><span data-omnivore-anchor-idx="774" class="hljs-attribute">zsh-completion</span></code> as a <a data-omnivore-anchor-idx="775" href="https://github.com/Phantas0s/.dotfiles/blob/master/.gitmodules" target="_blank" rel="noopener">git submodule in my dotfiles</a>. Then, you can automatically add every completion to your <code data-omnivore-anchor-idx="776" class="hljs language-ebnf"><span data-omnivore-anchor-idx="777" class="hljs-attribute">fpath</span></code>, in your zshrc:</p><div data-omnivore-anchor-idx="778"><pre data-omnivore-anchor-idx="779" tabindex="0"><code data-omnivore-anchor-idx="780" class="hljs language-elixir language-ruby">fpath=(<span data-omnivore-anchor-idx="781" class="hljs-regexp">/path/to</span><span data-omnivore-anchor-idx="782" class="hljs-regexp">/my/zsh</span><span data-omnivore-anchor-idx="783" class="hljs-regexp">/plugins/zsh</span>-completions/src <span data-omnivore-anchor-idx="784" class="hljs-variable">$fpath</span>)
  </code></pre></div><p data-omnivore-anchor-idx="785">You don’t need to load every completion file, one by one. If you look at the beginning of one of these files, you’ll see <code data-omnivore-anchor-idx="786" class="hljs language-ebnf"><span data-omnivore-anchor-idx="787" class="hljs-attribute">compdef</span></code>. It’s a function from Zsh which load automagically the completion when it’s needed. The completion file itself only needs to be included in your <code data-omnivore-anchor-idx="788" class="hljs language-ebnf"><span data-omnivore-anchor-idx="789" class="hljs-attribute">fpath</span></code>.</p><p data-omnivore-anchor-idx="790">You can also cherry-pick the specific completions you want.</p><h3 data-omnivore-anchor-idx="791" id="zsh-syntax-highlighting">Zsh Syntax Highlighting</h3><p data-omnivore-anchor-idx="792">What about syntax highlighting in Zsh? That’s what <a data-omnivore-anchor-idx="793" href="https://github.com/zsh-users/zsh-syntax-highlighting" target="_blank" rel="noopener">zsh-syntax-highlighting</a> is about.</p><p data-omnivore-anchor-idx="794">You can source it directly:</p><div data-omnivore-anchor-idx="795"><pre data-omnivore-anchor-idx="796" tabindex="0"><code data-omnivore-anchor-idx="797" class="hljs language-vim language-dts"><span data-omnivore-anchor-idx="798" class="hljs-keyword">source</span> /path/<span data-omnivore-anchor-idx="799" class="hljs-keyword">to</span>/my/zsh/plugins/zsh-<span data-omnivore-anchor-idx="800" class="hljs-keyword">syntax</span>-highlighting/zsh-<span data-omnivore-anchor-idx="801" class="hljs-keyword">syntax</span>-highlighting.zsh
  </code></pre></div><p data-omnivore-anchor-idx="802">There is one minor downside however: it seems to be currently incompatible with the surround widget we’ve seen above. If you want to use both, you need to use the branch <a data-omnivore-anchor-idx="803" href="https://github.com/zsh-users/zsh-syntax-highlighting/tree/feature/redrawhook" target="_blank" rel="noopener">feature/redrawhook</a>.</p><p data-omnivore-anchor-idx="804">You should source this plugin at the bottom of your <code data-omnivore-anchor-idx="805" class="hljs language-ebnf"><span data-omnivore-anchor-idx="806" class="hljs-attribute">zshrc</span></code>. Everything loaded before will then be able to use syntax highlighting if needed.</p><h3 data-omnivore-anchor-idx="807" id="jumping-to-a-parent-directory-easily">Jumping To A Parent Directory Easily</h3><p data-omnivore-anchor-idx="808">Do you like to type <code data-omnivore-anchor-idx="809" class="hljs language-routeros language-gams">cd <span data-omnivore-anchor-idx="810" class="hljs-built_in">..</span>/<span data-omnivore-anchor-idx="811" class="hljs-built_in">..</span>/<span data-omnivore-anchor-idx="812" class="hljs-built_in">..</span></code> to come back to the great-grand-parent of the current folder?</p><p data-omnivore-anchor-idx="813">Me neither.</p><p data-omnivore-anchor-idx="814">It’s where <a data-omnivore-anchor-idx="815" href="https://github.com/Tarrasch/zsh-bd" target="_blank" rel="noopener">bd</a> can help you. Imagine that you’re in the folder <code data-omnivore-anchor-idx="816" class="hljs language-awk language-crystal">~<span data-omnivore-anchor-idx="817" class="hljs-regexp">/a/</span>b<span data-omnivore-anchor-idx="818" class="hljs-regexp">/c/</span>d</code>. You can jump directly to <code data-omnivore-anchor-idx="819" class="hljs language-ebnf language-livecodeserver"><span data-omnivore-anchor-idx="820" class="hljs-attribute">a</span></code> with the command <code data-omnivore-anchor-idx="821" class="hljs language-armasm language-ebnf"><span data-omnivore-anchor-idx="822" class="hljs-keyword">bd </span>a</code>.</p><p data-omnivore-anchor-idx="823">The Zsh completion is even included. Awesomeness!</p><p data-omnivore-anchor-idx="824">To use it, you need to source the file <a data-omnivore-anchor-idx="825" href="https://github.com/Tarrasch/zsh-bd/blob/master/bd.zsh" target="_blank" rel="noopener">bd.zsh</a>.</p><h2 data-omnivore-anchor-idx="826" id="custom-scripts">Custom Scripts</h2><p data-omnivore-anchor-idx="827">Using a shell allows you to automate many parts of your workflow with shell scripts. That’s a huge benefit you should take advantage of.</p><p data-omnivore-anchor-idx="828">I keep most of <a data-omnivore-anchor-idx="829" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/scripts.zsh" target="_blank" rel="noopener">my scripts in one file</a> and I <a data-omnivore-anchor-idx="830" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/README.md" target="_blank" rel="noopener">document them</a> (roughly) for me to remember what’s in there, and for others to get inspired.</p><p data-omnivore-anchor-idx="831">I source the functions in my <code data-omnivore-anchor-idx="832" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="833" class="hljs-title">.zshrc</span></code>, but you could autoload them too.</p><p data-omnivore-anchor-idx="834">While working, ask yourself what tasks you do again and again, to automate them as much as you can. This is the real power of the shell, and it will make your whole workflow more fun.</p><h2 data-omnivore-anchor-idx="835" id="external-programs">External Programs</h2><p data-omnivore-anchor-idx="836">A shell without CLIs would be useless. Here are my personal favorites to expand Zsh functionalities.</p><h3 data-omnivore-anchor-idx="837" id="multiplex-your-zsh-with-tmux">Multiplex Your Zsh With tmux</h3><p data-omnivore-anchor-idx="838">I’ve already <a data-omnivore-anchor-idx="839" href="https://thevaluable.dev/tmux-boost-productivity-terminal/">written about tmux here</a>. It’s a terminal multiplexer with a tonne of functionalities: you can split your terminal in many windows or panes, synchronize them, and keep your sessions alive even without terminal. You can even extend it with plugins helping you automating your whole shell workflow.</p><h3 data-omnivore-anchor-idx="840" id="fuzzy-search-with-fzf">Fuzzy Search With fzf</h3><p data-omnivore-anchor-idx="841">The fuzzy finder <code data-omnivore-anchor-idx="842" class="hljs language-ebnf"><span data-omnivore-anchor-idx="843" class="hljs-attribute">fzf</span></code> is a fast and powerful tool. You can use it to search anything you want, like a file, an entry in your command line history, or a specific git commit message.</p><p data-omnivore-anchor-idx="844">I wrote (or copied and pasted) a bunch of <a data-omnivore-anchor-idx="845" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/scripts_fzf.zsh" target="_blank" rel="noopener">scripts using zsh</a> too, to search through git logs or <code data-omnivore-anchor-idx="846" class="hljs language-ebnf"><span data-omnivore-anchor-idx="847" class="hljs-attribute">tmuxp</span></code> projects.</p><p data-omnivore-anchor-idx="848">There are different ways to install <code data-omnivore-anchor-idx="849" class="hljs language-ebnf"><span data-omnivore-anchor-idx="850" class="hljs-attribute">fzf</span></code>. You’ll need first the executable. Then, I would recommend sourcing the files:</p><ul data-omnivore-anchor-idx="851"><li data-omnivore-anchor-idx="852"><code data-omnivore-anchor-idx="853" class="hljs language-css language-gauss"><span data-omnivore-anchor-idx="854" class="hljs-selector-tag">key-bindings</span><span data-omnivore-anchor-idx="855" class="hljs-selector-class">.zsh</span></code>, which will include some practical keystrokes like <code data-omnivore-anchor-idx="856" class="hljs language-ebnf"><span data-omnivore-anchor-idx="857" class="hljs-attribute">Ctrl-h</span></code> or <code data-omnivore-anchor-idx="858" class="hljs language-ebnf language-excel"><span data-omnivore-anchor-idx="859" class="hljs-attribute">Ctrl-t</span></code></li><li data-omnivore-anchor-idx="860"><code data-omnivore-anchor-idx="861" class="hljs language-css"><span data-omnivore-anchor-idx="862" class="hljs-selector-tag">completion</span><span data-omnivore-anchor-idx="863" class="hljs-selector-class">.zsh</span></code>, for <code data-omnivore-anchor-idx="864" class="hljs language-ebnf"><span data-omnivore-anchor-idx="865" class="hljs-attribute">fzf</span></code> completion.</li></ul><p data-omnivore-anchor-idx="866">If you use Arch Linux, you’ll need to install the package <code data-omnivore-anchor-idx="867" class="hljs language-ebnf"><span data-omnivore-anchor-idx="868" class="hljs-attribute">fzf</span></code> and simply source these two files in your <code data-omnivore-anchor-idx="869" class="hljs language-ebnf"><span data-omnivore-anchor-idx="870" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="871"><pre data-omnivore-anchor-idx="872" tabindex="0"><code data-omnivore-anchor-idx="873" class="hljs language-gradle language-awk"><span data-omnivore-anchor-idx="874" class="hljs-keyword">source</span> <span data-omnivore-anchor-idx="875" class="hljs-regexp">/usr/</span>share<span data-omnivore-anchor-idx="876" class="hljs-regexp">/fzf/</span>completion.zsh
  <span data-omnivore-anchor-idx="877" class="hljs-keyword">source</span> <span data-omnivore-anchor-idx="878" class="hljs-regexp">/usr/</span>share<span data-omnivore-anchor-idx="879" class="hljs-regexp">/fzf/</span>key-bindings.zsh
  </code></pre></div><p data-omnivore-anchor-idx="880">Otherwise, you’ll need to follow the installation process from fzf’s README file.</p><h2 data-omnivore-anchor-idx="881" id="the-z-shell-is-now-yours">The Z-Shell Is Now Yours</h2><p data-omnivore-anchor-idx="882">You should now have a clean and lean Zsh configuration, and you should understand enough of it to customize it.</p><p data-omnivore-anchor-idx="883">What did we learn with this article?</p><ul data-omnivore-anchor-idx="884"><li data-omnivore-anchor-idx="885">Zsh reads its configuration files in a precise order.</li><li data-omnivore-anchor-idx="886">You can set (or unset) many Zsh options depending on your needs.</li><li data-omnivore-anchor-idx="887">The completion system of Zsh is one of its best feature.</li><li data-omnivore-anchor-idx="888">Zsh directory stack allow you to jump easily in directories you’ve already visited.</li><li data-omnivore-anchor-idx="889">If you like Vim, Zsh allows you to use keystrokes from the Vim world. You can even edit your commands directly in Vim.</li><li data-omnivore-anchor-idx="890">External plugins can be found on The Internet, to improve even further the Zsh experience.</li><li data-omnivore-anchor-idx="891">You should go crazy on shell scripting, to automate your workflow as much as you can.</li><li data-omnivore-anchor-idx="892">External programs can enhance your experience with the shell, like <code data-omnivore-anchor-idx="893" class="hljs language-ebnf"><span data-omnivore-anchor-idx="894" class="hljs-attribute">tmux</span></code> or <code data-omnivore-anchor-idx="895" class="hljs language-ebnf"><span data-omnivore-anchor-idx="896" class="hljs-attribute">fzf</span></code>.</li></ul><p data-omnivore-anchor-idx="897">All your colleagues will be jealous. Guaranteed.</p></section></article></main></div></DIV></DIV>

raw_content:
  ---
  id: 191e19a9-a07e-479a-82bc-52f90227746c
  title: |
    Configuring Zsh Without Dependencies
  status: ARCHIVED
  tags:
    - read-later
  date_added: 2024-02-15 10:01:52
  url_omnivore: |
    https://omnivore.app/me/https-thevaluable-dev-zsh-install-configure-mouseless-18dacdbdb29
  url_original: |
    https://thevaluable.dev/zsh-install-configure-mouseless/
  ---

  # Configuring Zsh Without Dependencies

  ## Highlights

  Zsh read these files in the following order:

  1. `.zshenv` \- Should only contain user’s environment variables.
  2. `.zprofile` \- Can be used to execute commands just after logging in.
  3. `.zshrc` \- Should be used for the shell configuration and for executing commands.
  4. `.zlogin` \- Same purpose than `.zprofile`, but read just after `.zshrc`.
  5. `.zlogout` \- Can be used to execute commands when a shell exit. 

  [source](https://omnivore.app/me/https-thevaluable-dev-zsh-install-configure-mouseless-18dacdbdb29#debc2c0b-4a8b-4073-8a0b-fbddbf99bdcb) 

  ---

  ## Original

  <DIV id="readability-content"><DIV data-omnivore-anchor-idx="1" class="page" id="readability-page-1"><div data-omnivore-anchor-idx="2"><main data-omnivore-anchor-idx="3" role="main"><article data-omnivore-anchor-idx="4"><header data-omnivore-anchor-idx="5"></header><section data-omnivore-anchor-idx="6"><picture data-omnivore-anchor-idx="7"><source data-omnivore-anchor-idx="8" srcset="https://proxy-prod.omnivore-image-cache.app/0x0,srshUvfS3G_ezR33RSwvxWA169d3kznE6QwTzqZ8P_6Y/https://thevaluable.dev/images/2020/zsh/zsh.webp," type="image/webp"><img data-omnivore-anchor-idx="9" data-omnivore-original-src="https://thevaluable.dev/images/2020/zsh/zsh.jpg" width="780" height="520" src="https://proxy-prod.omnivore-image-cache.app/780x520,sRAkOrK0pbDyfGGP8JZyhZzzaifzNMRVFQJs9ceY2rMQ/https://thevaluable.dev/images/2020/zsh/zsh.jpg" alt="Huey, Dewey, and Louie with a Z, S, and H t-shirt"></picture><p data-omnivore-anchor-idx="10">This article is part of a series about Zsh:</p><p data-omnivore-anchor-idx="11">There are many boring tasks we repeat day after day: creating, copying, moving or searching files, launching again and again the same tools, docker containers, and whatnot.</p><p data-omnivore-anchor-idx="12">For a developer, the shell is a precious asset which can increase your efficiency over time. It will bring powerful tools at your fingertips, and, more importantly, it will allow you to automate many parts of your workflow.</p><p data-omnivore-anchor-idx="13">To leverage these functionalities, you’ll need a powerful and flexible shell. Today, I would like to present your next best friend: the Z shell, or Zsh.</p><p data-omnivore-anchor-idx="14">If you look at the documentation (around 450 pages for the <a data-omnivore-anchor-idx="15" href="http://zsh.sourceforge.net/Doc/zsh_a4.pdf" target="_blank" rel="noopener">PDF version</a>), Zsh can feel daunting. There are so many options available, it can be difficult to come up with a basic configuration you can build upon.</p><p data-omnivore-anchor-idx="16">We’ll build, in this article, a basic Zsh config. I’ll explain the meaning of (almost) everything along the way, including:</p><ul data-omnivore-anchor-idx="17"><li data-omnivore-anchor-idx="18">What’s a Unix shell.</li><li data-omnivore-anchor-idx="19">Why Zsh is a good choice.</li><li data-omnivore-anchor-idx="20">How to install Zsh.</li><li data-omnivore-anchor-idx="21">A brief overview of:<ul data-omnivore-anchor-idx="22"><li data-omnivore-anchor-idx="23">Useful environment variables.</li><li data-omnivore-anchor-idx="24">Aliases.</li><li data-omnivore-anchor-idx="25">The Zsh options.</li><li data-omnivore-anchor-idx="26">The Zsh completion.</li><li data-omnivore-anchor-idx="27">The Zsh prompt.</li><li data-omnivore-anchor-idx="28">The Zsh directory stack.</li></ul></li><li data-omnivore-anchor-idx="29">How to configure Zsh to make it Vim-like.</li><li data-omnivore-anchor-idx="30">How to add external plugins to Zsh.</li><li data-omnivore-anchor-idx="31">External programs you can use to improve your Zsh experience.</li></ul><p data-omnivore-anchor-idx="32">Are your keyboard ready? Are you fingers warm? Did you stretch your arms? Let’s begin, then!</p><h2 data-omnivore-anchor-idx="33" id="brief-unix-shell-overview">Brief Unix Shell Overview</h2><p data-omnivore-anchor-idx="34">A shell <em data-omnivore-anchor-idx="35">interpret</em> command lines. You can type them using a prompt in an <em data-omnivore-anchor-idx="36">interactive shell</em>, or you can run shell scripts using a <em data-omnivore-anchor-idx="37">non-interactive shell</em>.</p><p data-omnivore-anchor-idx="38">The shell run just after you logged in with your user. You can imagine the shell as the layer directly above the kernel of Unix-based operating systems (including Linux). Here’s the charismatic <a data-omnivore-anchor-idx="39" href="https://youtu.be/tc4ROCJYbm0?t=248" target="_blank" rel="noopener">Brian Kernighan explaining it casually with his feet on a table</a>.</p><p data-omnivore-anchor-idx="40">When you use a graphical interface (or GUI), you click around with your mouse to perform tasks. When you use a shell, you use plain text instead.</p><p data-omnivore-anchor-idx="41">If you use a graphical interface (like a windows manager or a desktop environment), you’ll need a <em data-omnivore-anchor-idx="42">terminal emulator</em> to access the shell. In the old days, a <a data-omnivore-anchor-idx="43" href="https://en.wikipedia.org/wiki/Computer_terminal" target="_blank" rel="noopener">terminal was a real device</a>. Nowadays, it’s a program.</p><p data-omnivore-anchor-idx="44">The shell gives you access to many powerful programs. They are called CLIs, or Command Line Interfaces.</p><p data-omnivore-anchor-idx="45">At that point, you might wonder: why using a shell, instead of a graphical interface?</p><ul data-omnivore-anchor-idx="46"><li data-omnivore-anchor-idx="47">It’s difficult to get a graphical interface right, especially if your software has many functionalities. It can be simpler to build a CLI to avoid some complexity.</li><li data-omnivore-anchor-idx="48">CLIs are usually faster.</li><li data-omnivore-anchor-idx="49">A developer deals often with plain text. CLIs are great for that.</li><li data-omnivore-anchor-idx="50">Many shells, like Linux shells, allow you to pipe CLIs together in order to create a powerful transformation flow.</li><li data-omnivore-anchor-idx="51">It’s easier to automate textual commands rather than actions on a graphical interface.</li></ul><blockquote data-omnivore-anchor-idx="52"><p data-omnivore-anchor-idx="53">Play around with your command shell, and you’ll be surprised at how much more productive it makes you.</p></blockquote><p data-omnivore-anchor-idx="54">A shell is the keystone of a Mouseless Development Environment, and the most powerful tool you can use as a developer.</p><h2 data-omnivore-anchor-idx="55" id="bash-vs-zsh">Bash vs Zsh</h2><p data-omnivore-anchor-idx="56">There are other Linux shells available out there, including the famous <a data-omnivore-anchor-idx="57" href="https://en.wikipedia.org/wiki/Bash_%28Unix_shell%29" target="_blank" rel="noopener">Bash</a>. Why using Zsh?</p><ul data-omnivore-anchor-idx="58"><li data-omnivore-anchor-idx="59">The level of flexibility and customization of Zsh is crazy.</li><li data-omnivore-anchor-idx="60">You have access to a powerful completion for your favorite CLIs.</li><li data-omnivore-anchor-idx="61">The Vi mode is golden for every Vim lovers.</li><li data-omnivore-anchor-idx="62">There is an important and active community around Zsh.</li><li data-omnivore-anchor-idx="63">Bash scripts are (mostly) compatible with Zsh.</li></ul><p data-omnivore-anchor-idx="64">Bash is simpler than Zsh, but it has also less functionalities.</p><h2 data-omnivore-anchor-idx="65" id="zsh-without-oh-my-zsh">Zsh Without oh-my-zsh</h2><p data-omnivore-anchor-idx="66">You’ll see many advising you to install a Zsh framework with a crazy number of plugins, options, aliases, all already configured. The famous ones are <a data-omnivore-anchor-idx="67" href="https://ohmyz.sh/" target="_blank" rel="noopener">Oh My Zsh</a> and <a data-omnivore-anchor-idx="68" href="https://github.com/sorin-ionescu/prezto" target="_blank" rel="noopener">prezto</a>.</p><p data-omnivore-anchor-idx="69">I tried this approach for years and I think the drawbacks outweigh the benefits:</p><ul data-omnivore-anchor-idx="70"><li data-omnivore-anchor-idx="71">I have no clue what’s included in these frameworks. When I read their documentations, I can’t possibly remember everything it sets. Therefore, I barely use 10% of the functionalities.</li><li data-omnivore-anchor-idx="72">Zsh has already many functionalities and options, it’s even more daunting to have a framework on top.</li><li data-omnivore-anchor-idx="73">A framework is a big external dependency which brings more complexity. If there is a conflict with my own configuration or a bug, it can take a long time to figure out what’s happening.</li><li data-omnivore-anchor-idx="74">A framework impose rules and way of doing I don’t necessarily want, or need.</li></ul><p data-omnivore-anchor-idx="75">Don’t get me wrong: these frameworks are incredible feats. They can be useful to get some inspiration for your own configuration. But I wouldn’t use them directly.</p><h2 data-omnivore-anchor-idx="76" id="let-the-party-begin">Let The Party Begin</h2><p data-omnivore-anchor-idx="77">We’ll now configure Zsh. If the files or folders I’m speaking about don’t exist, you need to create them.</p><p data-omnivore-anchor-idx="78">This configuration was tested with a Linux based system. I have no idea about macOS, but it should work.</p><h3 data-omnivore-anchor-idx="79" id="installing-zsh">Installing Zsh</h3><p data-omnivore-anchor-idx="80">You can install Zsh like everything else:</p><ul data-omnivore-anchor-idx="81"><li data-omnivore-anchor-idx="82">Debian / Ubuntu: <code data-omnivore-anchor-idx="83" class="hljs language-cmake language-ebnf">sudo apt <span data-omnivore-anchor-idx="84" class="hljs-keyword">install</span> zsh</code></li><li data-omnivore-anchor-idx="85">Red Hat: <code data-omnivore-anchor-idx="86" class="hljs language-cmake language-ebnf">sudo yum <span data-omnivore-anchor-idx="87" class="hljs-keyword">install</span> zsh</code></li><li data-omnivore-anchor-idx="88">Arch Linux: <code data-omnivore-anchor-idx="89" class="hljs language-ebnf language-nginx"><span data-omnivore-anchor-idx="90" class="hljs-attribute">sudo pacman -S zsh</span></code></li><li data-omnivore-anchor-idx="91">macOS (with brew): <code data-omnivore-anchor-idx="92" class="hljs language-mipsasm language-armasm"><span data-omnivore-anchor-idx="93" class="hljs-keyword">brew </span><span data-omnivore-anchor-idx="94" class="hljs-keyword">install </span>zsh</code></li></ul><p data-omnivore-anchor-idx="95">Then, run it in a terminal by typing <code data-omnivore-anchor-idx="96" class="hljs language-ebnf"><span data-omnivore-anchor-idx="97" class="hljs-attribute">zsh</span></code>.</p><h3 data-omnivore-anchor-idx="98" id="zsh-config-files">Zsh Config Files</h3><p data-omnivore-anchor-idx="99">To configure Zsh for your user’s session, you can use the following files:</p><ul data-omnivore-anchor-idx="100"><li data-omnivore-anchor-idx="101"><code data-omnivore-anchor-idx="102" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="103" class="hljs-meta"><span data-omnivore-anchor-idx="104" class="hljs-meta-keyword">$ZDOTDIR</span>/.zshenv</span></code></li><li data-omnivore-anchor-idx="105"><code data-omnivore-anchor-idx="106" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="107" class="hljs-meta"><span data-omnivore-anchor-idx="108" class="hljs-meta-keyword">$ZDOTDIR</span>/.zprofile</span></code></li><li data-omnivore-anchor-idx="109"><code data-omnivore-anchor-idx="110" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="111" class="hljs-meta"><span data-omnivore-anchor-idx="112" class="hljs-meta-keyword">$ZDOTDIR</span>/.zshrc</span></code></li><li data-omnivore-anchor-idx="113"><code data-omnivore-anchor-idx="114" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="115" class="hljs-meta"><span data-omnivore-anchor-idx="116" class="hljs-meta-keyword">$ZDOTDIR</span>/.zlogin</span></code></li><li data-omnivore-anchor-idx="117"><code data-omnivore-anchor-idx="118" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="119" class="hljs-meta"><span data-omnivore-anchor-idx="120" class="hljs-meta-keyword">$ZDOTDIR</span>/.zlogout</span></code></li></ul><p data-omnivore-anchor-idx="121">In case you wonder what <code data-omnivore-anchor-idx="122" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="123" class="hljs-meta"><span data-omnivore-anchor-idx="124" class="hljs-meta-keyword">$ZDOTDIR</span></span></code> stands for, we’ll come back to it soon.</p><p data-omnivore-anchor-idx="125">Zsh read these files in the following order:</p><ol data-omnivore-anchor-idx="126"><li data-omnivore-anchor-idx="127"><code data-omnivore-anchor-idx="128" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="129" class="hljs-title">.zshenv</span></code> - Should only contain user’s environment variables.</li><li data-omnivore-anchor-idx="130"><code data-omnivore-anchor-idx="131" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="132" class="hljs-title">.zprofile</span></code> - Can be used to execute commands just after logging in.</li><li data-omnivore-anchor-idx="133"><code data-omnivore-anchor-idx="134" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="135" class="hljs-title">.zshrc</span></code> - Should be used for the shell configuration and for executing commands.</li><li data-omnivore-anchor-idx="136"><code data-omnivore-anchor-idx="137" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="138" class="hljs-title">.zlogin</span></code> - Same purpose than <code data-omnivore-anchor-idx="139" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="140" class="hljs-title">.zprofile</span></code>, but read just after <code data-omnivore-anchor-idx="141" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="142" class="hljs-title">.zshrc</span></code>.</li><li data-omnivore-anchor-idx="143"><code data-omnivore-anchor-idx="144" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="145" class="hljs-title">.zlogout</span></code> - Can be used to execute commands when a shell exit.</li></ol><p data-omnivore-anchor-idx="146">We’ll use only <code data-omnivore-anchor-idx="147" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="148" class="hljs-title">.zshenv</span></code> and <code data-omnivore-anchor-idx="149" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="150" class="hljs-title">.zshrc</span></code> in this article.</p><h3 data-omnivore-anchor-idx="151" id="zsh-config-path">Zsh Config Path</h3><p data-omnivore-anchor-idx="152">By default, Zsh will try to find the user’s configuration files in the <code data-omnivore-anchor-idx="153" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="154" class="hljs-meta"><span data-omnivore-anchor-idx="155" class="hljs-meta-keyword">$HOME</span></span></code> directory. You can change it by setting the environment variable <code data-omnivore-anchor-idx="156" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="157" class="hljs-meta"><span data-omnivore-anchor-idx="158" class="hljs-meta-keyword">$ZDOTDIR</span></span></code>.</p><p data-omnivore-anchor-idx="159">Personally, I like to have all my configuration files in <code data-omnivore-anchor-idx="160" class="hljs language-gams language-arduino"><span data-omnivore-anchor-idx="161" class="hljs-meta"><span data-omnivore-anchor-idx="162" class="hljs-meta-keyword">$HOME</span>/.config</span></code>. To do so:</p><ol data-omnivore-anchor-idx="163"><li data-omnivore-anchor-idx="164">I set the variable <code data-omnivore-anchor-idx="165" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="166" class="hljs-meta"><span data-omnivore-anchor-idx="167" class="hljs-meta-keyword">$XDG</span>_CONFIG_HOME</span></code> as following: <code data-omnivore-anchor-idx="168" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="169" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="170" class="hljs-attribute">XDG_CONFIG_HOME</span>=<span data-omnivore-anchor-idx="171" class="hljs-string">"<span data-omnivore-anchor-idx="172" class="hljs-variable">$HOME</span>/.config"</span></code>.</li><li data-omnivore-anchor-idx="173">I set the environment variable <code data-omnivore-anchor-idx="174" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="175" class="hljs-meta"><span data-omnivore-anchor-idx="176" class="hljs-meta-keyword">$ZDOTDIR</span></span></code>: <code data-omnivore-anchor-idx="177" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="178" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="179" class="hljs-attribute">ZDOTDIR</span>=<span data-omnivore-anchor-idx="180" class="hljs-string">"<span data-omnivore-anchor-idx="181" class="hljs-variable">$XDG_CONFIG_HOME</span>/zsh"</span></code>.</li><li data-omnivore-anchor-idx="182">I put the file <code data-omnivore-anchor-idx="183" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="184" class="hljs-title">.zshrc</span></code> in the <code data-omnivore-anchor-idx="185" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="186" class="hljs-meta"><span data-omnivore-anchor-idx="187" class="hljs-meta-keyword">$ZDOTDIR</span></span></code> directory.</li></ol><p data-omnivore-anchor-idx="188">Most software will use the path in <code data-omnivore-anchor-idx="189" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="190" class="hljs-meta"><span data-omnivore-anchor-idx="191" class="hljs-meta-keyword">$XDG</span>_CONFIG_HOME</span></code> to install their own config files. As a result, you’ll have a clean <code data-omnivore-anchor-idx="192" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="193" class="hljs-meta"><span data-omnivore-anchor-idx="194" class="hljs-meta-keyword">$HOME</span></span></code> directory.</p><p data-omnivore-anchor-idx="195">Unfortunately, the file <code data-omnivore-anchor-idx="196" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="197" class="hljs-title">.zshenv</span></code> <strong data-omnivore-anchor-idx="198">needs to be in your home directory</strong>. It’s where you’ll set <code data-omnivore-anchor-idx="199" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="200" class="hljs-meta"><span data-omnivore-anchor-idx="201" class="hljs-meta-keyword">$ZDOTDIR</span></span></code>. Then, every file read after <code data-omnivore-anchor-idx="202" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="203" class="hljs-title">.zshenv</span></code> can go into your <code data-omnivore-anchor-idx="204" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="205" class="hljs-meta"><span data-omnivore-anchor-idx="206" class="hljs-meta-keyword">$ZDOTDIR</span></span></code> directory.</p><h2 data-omnivore-anchor-idx="207" id="zsh-basic-config">Zsh Basic Config</h2><h3 data-omnivore-anchor-idx="208" id="environment-variables">Environment Variables</h3><p data-omnivore-anchor-idx="209">As we saw, you can set the environment variables you need for your user’s session in the file <code data-omnivore-anchor-idx="210" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="211" class="hljs-meta"><span data-omnivore-anchor-idx="212" class="hljs-meta-keyword">$HOME</span>/.zshenv</span></code>. This file should only define environment variables.</p><p data-omnivore-anchor-idx="213">For example, you can set up the <a data-omnivore-anchor-idx="214" href="https://wiki.archlinux.org/index.php/XDG_Base_Directory" target="_blank" rel="noopener">XDG Base directory</a> there, as seen above:</p><div data-omnivore-anchor-idx="215"><pre data-omnivore-anchor-idx="216" tabindex="0"><code data-omnivore-anchor-idx="217" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="218" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="219" class="hljs-attribute">XDG_CONFIG_HOME</span>=<span data-omnivore-anchor-idx="220" class="hljs-string">"<span data-omnivore-anchor-idx="221" class="hljs-variable">$HOME</span>/.config"</span>
  <span data-omnivore-anchor-idx="222" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="223" class="hljs-attribute">XDG_DATA_HOME</span>=<span data-omnivore-anchor-idx="224" class="hljs-string">"<span data-omnivore-anchor-idx="225" class="hljs-variable">$XDG_CONFIG_HOME</span>/local/share"</span>
  <span data-omnivore-anchor-idx="226" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="227" class="hljs-attribute">XDG_CACHE_HOME</span>=<span data-omnivore-anchor-idx="228" class="hljs-string">"<span data-omnivore-anchor-idx="229" class="hljs-variable">$XDG_CONFIG_HOME</span>/cache"</span>
  </code></pre></div><p data-omnivore-anchor-idx="230">You can also make sure that any program requiring a text editor use your favorite one:</p><div data-omnivore-anchor-idx="231"><pre data-omnivore-anchor-idx="232" tabindex="0"><code data-omnivore-anchor-idx="233" class="hljs language-routeros language-cpp"><span data-omnivore-anchor-idx="234" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="235" class="hljs-attribute">EDITOR</span>=<span data-omnivore-anchor-idx="236" class="hljs-string">"nvim"</span>
  <span data-omnivore-anchor-idx="237" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="238" class="hljs-attribute">VISUAL</span>=<span data-omnivore-anchor-idx="239" class="hljs-string">"nvim"</span>
  </code></pre></div><p data-omnivore-anchor-idx="240">You can set some Zsh environment variables, too:</p><div data-omnivore-anchor-idx="241"><pre data-omnivore-anchor-idx="242" tabindex="0"><code data-omnivore-anchor-idx="243" class="hljs language-routeros language-bash"><span data-omnivore-anchor-idx="244" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="245" class="hljs-attribute">ZDOTDIR</span>=<span data-omnivore-anchor-idx="246" class="hljs-string">"<span data-omnivore-anchor-idx="247" class="hljs-variable">$XDG_CONFIG_HOME</span>/zsh"</span>

  <span data-omnivore-anchor-idx="248" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="249" class="hljs-attribute">HISTFILE</span>=<span data-omnivore-anchor-idx="250" class="hljs-string">"<span data-omnivore-anchor-idx="251" class="hljs-variable">$ZDOTDIR</span>/.zhistory"</span>    # History filepath
  <span data-omnivore-anchor-idx="252" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="253" class="hljs-attribute">HISTSIZE</span>=10000                   # Maximum events <span data-omnivore-anchor-idx="254" class="hljs-keyword">for</span> internal history
  <span data-omnivore-anchor-idx="255" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="256" class="hljs-attribute">SAVEHIST</span>=10000                   # Maximum events <span data-omnivore-anchor-idx="257" class="hljs-keyword">in</span> history file
  </code></pre></div><p data-omnivore-anchor-idx="258">I already explained the first line. For the other ones, they will:</p><ul data-omnivore-anchor-idx="259"><li data-omnivore-anchor-idx="260">Store your command line history in the file <code data-omnivore-anchor-idx="261" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="262" class="hljs-title">.zhistory</span></code>.</li><li data-omnivore-anchor-idx="263">Allows you to have a history of 10000 entries maximum.</li></ul><p data-omnivore-anchor-idx="264"><a data-omnivore-anchor-idx="265" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/zshenv" target="_blank" rel="noopener">Here’s my .zshenv file</a>, if you need some inspiration.</p><h3 data-omnivore-anchor-idx="266" id="aliases">Aliases</h3><p data-omnivore-anchor-idx="267">Aliases are crucial to improve your efficiency. For example, I have a bunch of aliases for git I use all the time. It’s always easier to type when it’s shorter:</p><div data-omnivore-anchor-idx="268"><pre data-omnivore-anchor-idx="269" tabindex="0"><code data-omnivore-anchor-idx="270" class="hljs language-vhdl language-monkey"><span data-omnivore-anchor-idx="271" class="hljs-keyword">alias</span> gs=<span data-omnivore-anchor-idx="272" class="hljs-symbol">'git</span> status'
  <span data-omnivore-anchor-idx="273" class="hljs-keyword">alias</span> ga=<span data-omnivore-anchor-idx="274" class="hljs-symbol">'git</span> add'
  <span data-omnivore-anchor-idx="275" class="hljs-keyword">alias</span> gp=<span data-omnivore-anchor-idx="276" class="hljs-symbol">'git</span> push'
  <span data-omnivore-anchor-idx="277" class="hljs-keyword">alias</span> gpo=<span data-omnivore-anchor-idx="278" class="hljs-symbol">'git</span> push origin'
  <span data-omnivore-anchor-idx="279" class="hljs-keyword">alias</span> gtd=<span data-omnivore-anchor-idx="280" class="hljs-symbol">'git</span> tag <span data-omnivore-anchor-idx="281" class="hljs-comment">--delete'</span>
  <span data-omnivore-anchor-idx="282" class="hljs-keyword">alias</span> gtdr=<span data-omnivore-anchor-idx="283" class="hljs-symbol">'git</span> tag <span data-omnivore-anchor-idx="284" class="hljs-comment">--delete origin'</span>
  <span data-omnivore-anchor-idx="285" class="hljs-keyword">alias</span> gr=<span data-omnivore-anchor-idx="286" class="hljs-symbol">'git</span> branch -r'
  <span data-omnivore-anchor-idx="287" class="hljs-keyword">alias</span> gplo=<span data-omnivore-anchor-idx="288" class="hljs-symbol">'git</span> pull origin'
  <span data-omnivore-anchor-idx="289" class="hljs-keyword">alias</span> gb=<span data-omnivore-anchor-idx="290" class="hljs-symbol">'git</span> branch '
  <span data-omnivore-anchor-idx="291" class="hljs-keyword">alias</span> gc=<span data-omnivore-anchor-idx="292" class="hljs-symbol">'git</span> commit'
  <span data-omnivore-anchor-idx="293" class="hljs-keyword">alias</span> gd=<span data-omnivore-anchor-idx="294" class="hljs-symbol">'git</span> diff'
  <span data-omnivore-anchor-idx="295" class="hljs-keyword">alias</span> gco=<span data-omnivore-anchor-idx="296" class="hljs-symbol">'git</span> checkout '
  <span data-omnivore-anchor-idx="297" class="hljs-keyword">alias</span> gl=<span data-omnivore-anchor-idx="298" class="hljs-symbol">'git</span> log'
  <span data-omnivore-anchor-idx="299" class="hljs-keyword">alias</span> gr=<span data-omnivore-anchor-idx="300" class="hljs-symbol">'git</span> remote'
  <span data-omnivore-anchor-idx="301" class="hljs-keyword">alias</span> grs=<span data-omnivore-anchor-idx="302" class="hljs-symbol">'git</span> remote show'
  <span data-omnivore-anchor-idx="303" class="hljs-keyword">alias</span> glo=<span data-omnivore-anchor-idx="304" class="hljs-symbol">'git</span> log <span data-omnivore-anchor-idx="305" class="hljs-comment">--pretty="oneline"'</span>
  <span data-omnivore-anchor-idx="306" class="hljs-keyword">alias</span> glol=<span data-omnivore-anchor-idx="307" class="hljs-symbol">'git</span> log <span data-omnivore-anchor-idx="308" class="hljs-comment">--graph --oneline --decorate'</span>
  </code></pre></div><p data-omnivore-anchor-idx="309">I like to have my aliases in one separate file (called, surprisingly, <code data-omnivore-anchor-idx="310" class="hljs language-ebnf language-maxima"><span data-omnivore-anchor-idx="311" class="hljs-attribute">aliases</span></code>), and I source it in my <code data-omnivore-anchor-idx="312" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="313" class="hljs-title">.zshrc</span></code>:</p><div data-omnivore-anchor-idx="314"><pre data-omnivore-anchor-idx="315" tabindex="0"><code data-omnivore-anchor-idx="316" class="hljs language-gradle language-applescript"><span data-omnivore-anchor-idx="317" class="hljs-keyword">source</span> <span data-omnivore-anchor-idx="318" class="hljs-regexp">/path/</span>to<span data-omnivore-anchor-idx="319" class="hljs-regexp">/my/</span>aliases
  </code></pre></div><p data-omnivore-anchor-idx="320">Here are <a data-omnivore-anchor-idx="321" href="https://github.com/Phantas0s/.dotfiles/blob/master/aliases/aliases" target="_blank" rel="noopener">all my aliases</a>.</p><h3 data-omnivore-anchor-idx="322" id="zsh-options">Zsh Options</h3><p data-omnivore-anchor-idx="323">You can set or unset many <a data-omnivore-anchor-idx="324" href="http://zsh.sourceforge.net/Doc/Release/Options.html" target="_blank" rel="noopener">Zsh options</a> using <code data-omnivore-anchor-idx="325" class="hljs language-bash language-ebnf"><span data-omnivore-anchor-idx="326" class="hljs-built_in">setopt</span></code> or <code data-omnivore-anchor-idx="327" class="hljs language-bash language-ebnf"><span data-omnivore-anchor-idx="328" class="hljs-built_in">unsetopt</span></code>. For example:</p><div data-omnivore-anchor-idx="329"><pre data-omnivore-anchor-idx="330" tabindex="0"><code data-omnivore-anchor-idx="331" class="hljs language-bash language-delphi"><span data-omnivore-anchor-idx="332" class="hljs-built_in">setopt</span> HIST_SAVE_NO_DUPS         <span data-omnivore-anchor-idx="333" class="hljs-comment"># Do not write a duplicate event to the history file.</span>
  <span data-omnivore-anchor-idx="334" class="hljs-built_in">unsetopt</span> HIST_SAVE_NO_DUPS       <span data-omnivore-anchor-idx="335" class="hljs-comment"># Write a duplicate event to the history file</span>
  </code></pre></div><p data-omnivore-anchor-idx="336">You can already do a lot of customization only using these options.</p><h3 data-omnivore-anchor-idx="337" id="zsh-completion-system">Zsh Completion System</h3><p data-omnivore-anchor-idx="338">The completion system of Zsh is one of its bigger strength, compared to other shells.</p><p data-omnivore-anchor-idx="339">To initialize the completion for the current Zsh session, you’ll need to call the function <code data-omnivore-anchor-idx="340" class="hljs language-ebnf"><span data-omnivore-anchor-idx="341" class="hljs-attribute">compinit</span></code>. More precisely, you’ll need to add this in your <code data-omnivore-anchor-idx="342" class="hljs language-ebnf"><span data-omnivore-anchor-idx="343" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="344"><pre data-omnivore-anchor-idx="345" tabindex="0"><code data-omnivore-anchor-idx="346" class="hljs language-nginx language-abnf"><span data-omnivore-anchor-idx="347" class="hljs-attribute">autoload</span> -U compinit; <span data-omnivore-anchor-idx="348" class="hljs-attribute">compinit</span>
  </code></pre></div><p data-omnivore-anchor-idx="349">What does it mean?</p><p data-omnivore-anchor-idx="350">The <code data-omnivore-anchor-idx="351" class="hljs language-angelscript language-bash"><span data-omnivore-anchor-idx="352" class="hljs-built_in">auto</span>load</code> command load a file containing shell commands. To find this file, Zsh will look in the directories of the <em data-omnivore-anchor-idx="353">Zsh file search path</em>, defined in the variable <code data-omnivore-anchor-idx="354" class="hljs language-gams language-arcade"><span data-omnivore-anchor-idx="355" class="hljs-meta"><span data-omnivore-anchor-idx="356" class="hljs-meta-keyword">$fpath</span></span></code>, and search a file called <code data-omnivore-anchor-idx="357" class="hljs language-ebnf"><span data-omnivore-anchor-idx="358" class="hljs-attribute">compinit</span></code>.</p><p data-omnivore-anchor-idx="359">When <code data-omnivore-anchor-idx="360" class="hljs language-ebnf"><span data-omnivore-anchor-idx="361" class="hljs-attribute">compinit</span></code> is found, its content will be loaded as a <em data-omnivore-anchor-idx="362">function</em>. The function name will be the name of the file. You can then call this function like any other shell function.</p><div data-omnivore-anchor-idx="363"><p data-omnivore-anchor-idx="364">What about the semi-colon <code data-omnivore-anchor-idx="365" class="hljs language-abnf language-ini"><span data-omnivore-anchor-idx="366" class="hljs-comment">;</span></code>? It’s just a handy way to separate commands. It’s the same as calling <code data-omnivore-anchor-idx="367" class="hljs language-ebnf"><span data-omnivore-anchor-idx="368" class="hljs-attribute">compinit</span></code> on a new line.</p></div><p data-omnivore-anchor-idx="369">Why using autoload, and not sourcing the file by doing <code data-omnivore-anchor-idx="370" class="hljs language-gradle language-arcade"><span data-omnivore-anchor-idx="371" class="hljs-keyword">source</span> ~<span data-omnivore-anchor-idx="372" class="hljs-regexp">/path/</span>of<span data-omnivore-anchor-idx="373" class="hljs-regexp">/compinit</span></code>?</p><ul data-omnivore-anchor-idx="374"><li data-omnivore-anchor-idx="375">It avoids name conflicts if you have an executable with the same name.</li><li data-omnivore-anchor-idx="376">It doesn’t expand aliases thanks to the <code data-omnivore-anchor-idx="377" class="hljs language-diff language-haml"><span data-omnivore-anchor-idx="378" class="hljs-deletion">-U</span></code> option.</li><li data-omnivore-anchor-idx="379">It will load the function only when it’s needed (lazy-loading). It comes in handy to speed up Zsh startup.</li></ul><p data-omnivore-anchor-idx="380">Then, let’s add the following;</p><div data-omnivore-anchor-idx="381"><pre data-omnivore-anchor-idx="382" tabindex="0"><code data-omnivore-anchor-idx="383" class="hljs language-applescript language-dts">_comp_options+=(globdots) <span data-omnivore-anchor-idx="384" class="hljs-comment"># With hidden files</span>
  source /<span data-omnivore-anchor-idx="385" class="hljs-keyword">my</span>/path/<span data-omnivore-anchor-idx="386" class="hljs-keyword">to</span>/zsh/completion.zsh
  </code></pre></div><p data-omnivore-anchor-idx="387">The first line will complete <a data-omnivore-anchor-idx="388" href="https://wiki.archlinux.org/index.php/Dotfiles" target="_blank" rel="noopener">dotfiles</a>.</p><p data-omnivore-anchor-idx="389">The second line source <a data-omnivore-anchor-idx="390" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/completion.zsh" target="_blank" rel="noopener">this file</a>. It’s my personal config for the Zsh completion. I’ve written an <a data-omnivore-anchor-idx="391" href="https://thevaluable.dev/zsh-completion-guide-examples/">article about that</a> if you’re interested to dive more into the completion system.</p><p data-omnivore-anchor-idx="392">Now, the completion should work:</p><ul data-omnivore-anchor-idx="393"><li data-omnivore-anchor-idx="394">If you type <code data-omnivore-anchor-idx="395" class="hljs language-avrasm language-ebnf"><span data-omnivore-anchor-idx="396" class="hljs-keyword">cp</span></code> and hit the tab key, you’ll see that Zsh will complete the command.</li><li data-omnivore-anchor-idx="397">If you type <code data-omnivore-anchor-idx="398" class="hljs language-avrasm language-nginx"><span data-omnivore-anchor-idx="399" class="hljs-keyword">cp</span> -</code> and hit the tab key, Zsh will display the possible arguments for the command.</li></ul><picture data-omnivore-anchor-idx="400"><source data-omnivore-anchor-idx="401" srcset="https://proxy-prod.omnivore-image-cache.app/0x0,sfiBcmObf97NIeUibtBNabIdkPJ0sEqndbvVxv52zQmk/https://thevaluable.dev/images/2020/zsh/auto_complete.webp," type="image/webp"><img data-omnivore-anchor-idx="402" data-omnivore-original-src="https://thevaluable.dev/images/2020/zsh/auto_complete.png" width="780" height="520" src="https://proxy-prod.omnivore-image-cache.app/780x520,shXcZ-OuRAMljwivxvaLoPqZ9COFjw6mY9KEnyqQBCKQ/https://thevaluable.dev/images/2020/zsh/auto_complete.png" alt="Zsh completion in action"></picture><h3 data-omnivore-anchor-idx="403" id="pimp-my-zsh-prompt">Pimp My Zsh Prompt</h3><p data-omnivore-anchor-idx="404">What would be the shell experience without a nice prompt? Dull. Tasteless. Depressing.</p><p data-omnivore-anchor-idx="405">Let’s be honest here: Zsh default prompt is ugly. We need to change it, before our eyes start crying some blood. My needs are simple:</p><ul data-omnivore-anchor-idx="406"><li data-omnivore-anchor-idx="407">The prompt needs to be on one line. I had display problems with two lines.</li><li data-omnivore-anchor-idx="408">The prompt needs to display some git info when necessary.</li></ul><p data-omnivore-anchor-idx="409">From there, I created <a data-omnivore-anchor-idx="410" href="https://github.com/Phantas0s/purification/blob/master/prompt_purification_setup" target="_blank" rel="noopener">my own prompt</a> from <a data-omnivore-anchor-idx="411" href="https://github.com/therealklanni/purity" target="_blank" rel="noopener">another one</a>. It looks like that:</p><picture data-omnivore-anchor-idx="412"><source data-omnivore-anchor-idx="413" srcset="https://proxy-prod.omnivore-image-cache.app/0x0,sEbG09G1juFPo63j_VjykyvXcIJNnKy9th3zqNYTNLns/https://thevaluable.dev/images/2020/zsh/prompt.webp," type="image/webp"><img data-omnivore-anchor-idx="414" data-omnivore-original-src="https://thevaluable.dev/images/2020/zsh/prompt.png" width="780" height="520" src="https://proxy-prod.omnivore-image-cache.app/780x520,sYO_QmAuKGoSirbH4ZiWqkffidw64lNbq8Ixd6jKGOew/https://thevaluable.dev/images/2020/zsh/prompt.png" alt="Zsh prompt"></picture><p data-omnivore-anchor-idx="415">If you open the prompt script, you’ll see that it’s pretty simple:</p><ul data-omnivore-anchor-idx="416"><li data-omnivore-anchor-idx="417">I set two environment variables: <code data-omnivore-anchor-idx="418" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="419" class="hljs-meta"><span data-omnivore-anchor-idx="420" class="hljs-meta-keyword">$PROMPT</span></span></code> and <code data-omnivore-anchor-idx="421" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="422" class="hljs-meta"><span data-omnivore-anchor-idx="423" class="hljs-meta-keyword">$RPROMPT</span></span></code>. The first one format the left prompt, the second display git information on the far right.</li><li data-omnivore-anchor-idx="424">You can add some formatting styles using, for example, <code data-omnivore-anchor-idx="425" class="hljs language-haml language-cos"><span data-omnivore-anchor-idx="426" class="hljs-tag">%<span data-omnivore-anchor-idx="427" class="hljs-selector-tag">F</span>{blue}</span>%f</code> to change the color, or <code data-omnivore-anchor-idx="428" class="hljs language-cos language-gcode"><span data-omnivore-anchor-idx="429" class="hljs-built_in">%Bmy</span>-cool-prompt<span data-omnivore-anchor-idx="430" class="hljs-built_in">%b</span></code> to make everything bold.</li></ul><p data-omnivore-anchor-idx="431">This prompt doesn’t need any external <a data-omnivore-anchor-idx="432" href="https://thevaluable.dev/cohesion-coupling-guide-examples/">dependency</a>. You can copy it right away and modify it as much as you want.</p><p data-omnivore-anchor-idx="433"><a data-omnivore-anchor-idx="434" href="http://zsh.sourceforge.net/Doc/Release/Prompt-Expansion.html" target="_blank" rel="noopener">Here’s everything you need, to create the prompt of your dream</a>.</p><p data-omnivore-anchor-idx="435">To load the prompt, you need to add something like that in your <code data-omnivore-anchor-idx="436" class="hljs language-ebnf"><span data-omnivore-anchor-idx="437" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="438"><pre data-omnivore-anchor-idx="439" tabindex="0"><code data-omnivore-anchor-idx="440" class="hljs language-applescript language-arcade">fpath=(/<span data-omnivore-anchor-idx="441" class="hljs-keyword">my</span>/path/<span data-omnivore-anchor-idx="442" class="hljs-keyword">to</span>/zsh/prompt $fpath)
  autoload -Uz name_of_the_prompt_file; name_of_the_prompt_file
  </code></pre></div><p data-omnivore-anchor-idx="443">The first line will add the folder containing the prompt to <code data-omnivore-anchor-idx="444" class="hljs language-gams language-arcade"><span data-omnivore-anchor-idx="445" class="hljs-meta"><span data-omnivore-anchor-idx="446" class="hljs-meta-keyword">$fpath</span></span></code>, as discussed above. It will also ensure that any function declared in the folder <code data-omnivore-anchor-idx="447" class="hljs language-applescript language-awk">/<span data-omnivore-anchor-idx="448" class="hljs-keyword">my</span>/path/<span data-omnivore-anchor-idx="449" class="hljs-keyword">to</span>/zsh/prompt</code> will overwrite every other ones with the same name, in other <code data-omnivore-anchor-idx="450" class="hljs language-ebnf"><span data-omnivore-anchor-idx="451" class="hljs-attribute">fpath</span></code> folders.</p><p data-omnivore-anchor-idx="452">The second line autoload the prompt itself.</p><p data-omnivore-anchor-idx="453">This prompt require <a data-omnivore-anchor-idx="454" href="https://fontawesome.com/v4.7.0/" target="_blank" rel="noopener">font awesome 4</a> for the git icons. You can download the font and install it, or you can change the icons.</p><h3 data-omnivore-anchor-idx="455" id="zsh-directory-stack">Zsh Directory Stack</h3><p data-omnivore-anchor-idx="456">Zsh has commands to <a data-omnivore-anchor-idx="457" href="http://zsh.sourceforge.net/Intro/intro_6.html" target="_blank" rel="noopener">push and pop directories on a directory stack</a>.</p><p data-omnivore-anchor-idx="458">By manipulating this stack, you can set up an history of directory visited, and be able to jump back to these directories.</p><p data-omnivore-anchor-idx="459">First, let’s set some options in your <code data-omnivore-anchor-idx="460" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="461" class="hljs-title">.zshrc</span></code>:</p><div data-omnivore-anchor-idx="462"><pre data-omnivore-anchor-idx="463" tabindex="0"><code data-omnivore-anchor-idx="464" class="hljs language-bash language-nginx"><span data-omnivore-anchor-idx="465" class="hljs-built_in">setopt</span> AUTO_PUSHD           <span data-omnivore-anchor-idx="466" class="hljs-comment"># Push the current directory visited on the stack.</span>
  <span data-omnivore-anchor-idx="467" class="hljs-built_in">setopt</span> PUSHD_IGNORE_DUPS    <span data-omnivore-anchor-idx="468" class="hljs-comment"># Do not store duplicates in the stack.</span>
  <span data-omnivore-anchor-idx="469" class="hljs-built_in">setopt</span> PUSHD_SILENT         <span data-omnivore-anchor-idx="470" class="hljs-comment"># Do not print the directory stack after pushd or popd.</span>
  </code></pre></div><p data-omnivore-anchor-idx="471">Then, you can create these aliases:</p><div data-omnivore-anchor-idx="472"><pre data-omnivore-anchor-idx="473" tabindex="0"><code data-omnivore-anchor-idx="474" class="hljs language-bash language-perl"><span data-omnivore-anchor-idx="475" class="hljs-built_in">alias</span> d=<span data-omnivore-anchor-idx="476" class="hljs-string">'dirs -v'</span>
  <span data-omnivore-anchor-idx="477" class="hljs-keyword">for</span> index ({1..9}) <span data-omnivore-anchor-idx="478" class="hljs-built_in">alias</span> <span data-omnivore-anchor-idx="479" class="hljs-string">"<span data-omnivore-anchor-idx="480" class="hljs-variable">$index</span>"</span>=<span data-omnivore-anchor-idx="481" class="hljs-string">"cd +<span data-omnivore-anchor-idx="482" class="hljs-variable">${index}</span>"</span>; <span data-omnivore-anchor-idx="483" class="hljs-built_in">unset</span> index
  </code></pre></div><p data-omnivore-anchor-idx="484">What does it do?</p><ul data-omnivore-anchor-idx="485"><li data-omnivore-anchor-idx="486">Every directory visited will populate the stack.</li><li data-omnivore-anchor-idx="487">When you use the alias <code data-omnivore-anchor-idx="488" class="hljs language-ebnf"><span data-omnivore-anchor-idx="489" class="hljs-attribute">d</span></code>, it will display the directories on the stack prefixed with a number.</li><li data-omnivore-anchor-idx="490">The line <code data-omnivore-anchor-idx="491" class="hljs language-perl language-bash"><span data-omnivore-anchor-idx="492" class="hljs-keyword">for</span> <span data-omnivore-anchor-idx="493" class="hljs-keyword">index</span> ({<span data-omnivore-anchor-idx="494" class="hljs-number">1</span>..<span data-omnivore-anchor-idx="495" class="hljs-number">9</span>}) alias <span data-omnivore-anchor-idx="496" class="hljs-string">"$index"</span>=<span data-omnivore-anchor-idx="497" class="hljs-string">"cd +<span data-omnivore-anchor-idx="498" class="hljs-subst">${<span data-omnivore-anchor-idx="499" class="hljs-keyword">index</span>}</span>"</span>; unset <span data-omnivore-anchor-idx="500" class="hljs-keyword">index</span></code> will create aliases from 1 to 9. They will allow you to jump directly in whatever directory on your stack.</li></ul><p data-omnivore-anchor-idx="501">For example, if you execute <code data-omnivore-anchor-idx="502" class="hljs language-angelscript language-lsl"><span data-omnivore-anchor-idx="503" class="hljs-number">1</span></code> in Zsh, you’ll jump to the directory prefixed with <code data-omnivore-anchor-idx="504" class="hljs language-angelscript language-lsl"><span data-omnivore-anchor-idx="505" class="hljs-number">1</span></code> in your stack list.</p><p data-omnivore-anchor-idx="506">You can also increase <code data-omnivore-anchor-idx="507" class="hljs language-angelscript language-lsl">index ({<span data-omnivore-anchor-idx="508" class="hljs-number">1.</span><span data-omnivore-anchor-idx="509" class="hljs-number">.9</span>})</code> to <code data-omnivore-anchor-idx="510" class="hljs language-angelscript language-lsl">index ({<span data-omnivore-anchor-idx="511" class="hljs-number">1.</span><span data-omnivore-anchor-idx="512" class="hljs-number">.100</span>})</code> for example, if you want to be able to jump back to 100 directories.</p><p data-omnivore-anchor-idx="513">For example, you can do that:</p><div data-omnivore-anchor-idx="514"><pre data-omnivore-anchor-idx="515" tabindex="0"><code data-omnivore-anchor-idx="516" class="hljs language-jboss-cli language-arduino">~ &gt; <span data-omnivore-anchor-idx="517" class="hljs-keyword">cd</span> <span data-omnivore-anchor-idx="518" class="hljs-string">.config</span>
  ~<span data-omnivore-anchor-idx="519" class="hljs-string">/.config</span> &gt; <span data-omnivore-anchor-idx="520" class="hljs-keyword">cd</span> devdash
  ~<span data-omnivore-anchor-idx="521" class="hljs-string">/.config/devdash</span> &gt; <span data-omnivore-anchor-idx="522" class="hljs-keyword">cd</span> <span data-omnivore-anchor-idx="523" class="hljs-string">..</span>
  ~<span data-omnivore-anchor-idx="524" class="hljs-string">/.config</span> &gt; <span data-omnivore-anchor-idx="525" class="hljs-keyword">cd</span> i3
  ~<span data-omnivore-anchor-idx="526" class="hljs-string">/.config/i3</span> &gt; <span data-omnivore-anchor-idx="527" class="hljs-keyword">cd</span> <span data-omnivore-anchor-idx="528" class="hljs-string">..</span>
  ~<span data-omnivore-anchor-idx="529" class="hljs-string">/.config</span> &gt; d
  0       ~<span data-omnivore-anchor-idx="530" class="hljs-string">/.config</span>
  1       ~<span data-omnivore-anchor-idx="531" class="hljs-string">/.config/i3</span>
  2       ~<span data-omnivore-anchor-idx="532" class="hljs-string">/.config/devdash</span>
  3       ~
  ~<span data-omnivore-anchor-idx="533" class="hljs-string">/.config</span> &gt; 2
  ~<span data-omnivore-anchor-idx="534" class="hljs-string">/.config/devdash</span> &gt; 
  </code></pre></div><h3 data-omnivore-anchor-idx="535" id="zsh-by-default">Zsh By Default</h3><p data-omnivore-anchor-idx="536">When you’re ready psychologically to set Zsh as your default shell, you can run these commands:</p><ul data-omnivore-anchor-idx="537"><li data-omnivore-anchor-idx="538">For Linux: <code data-omnivore-anchor-idx="539" class="hljs language-reasonml language-arcade">chsh -s <span data-omnivore-anchor-idx="540" class="hljs-constructor">$(<span data-omnivore-anchor-idx="541" class="hljs-params">which</span> <span data-omnivore-anchor-idx="542" class="hljs-params">zsh</span>)</span></code></li><li data-omnivore-anchor-idx="543">For macOS: <code data-omnivore-anchor-idx="544" class="hljs language-reasonml language-bash">sudo sh -c <span data-omnivore-anchor-idx="545" class="hljs-string">"echo $(which zsh) &gt;&gt; /etc/shells"</span><span data-omnivore-anchor-idx="546" class="hljs-operator"> &amp;&amp; </span>chsh -s <span data-omnivore-anchor-idx="547" class="hljs-constructor">$(<span data-omnivore-anchor-idx="548" class="hljs-params">which</span> <span data-omnivore-anchor-idx="549" class="hljs-params">zsh</span>)</span></code></li></ul><p data-omnivore-anchor-idx="550">A good soul on Reddit whispered me that Zsh is now the default shell from macOS Catalina onwards, so you don’t necessarily need the above command.</p><p data-omnivore-anchor-idx="551">Zsh is now part of your life. Congratulation!</p><h2 data-omnivore-anchor-idx="552" id="zsh-with-vim-flavors">Zsh With Vim Flavors</h2><p data-omnivore-anchor-idx="553">For editing purposes, <a data-omnivore-anchor-idx="554" href="https://thevaluable.dev/phpstorm-vs-vim/">Vim is my best friend</a>. I love when CLIs use some Vim key binding, and Zsh gives you even more than that. If you’d like to learn Vim, this <a data-omnivore-anchor-idx="555" href="https://thevaluable.dev/vim-commands-beginner/" target="_blank" rel="noopener">series of articles</a> can help.</p><h3 data-omnivore-anchor-idx="556" id="activating-vi-mode">Activating Vi Mode</h3><p data-omnivore-anchor-idx="557">Zsh has a Vi mode you can enable by adding the following in your <code data-omnivore-anchor-idx="558" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="559" class="hljs-title">.zshrc</span></code>:</p><div data-omnivore-anchor-idx="560"><pre data-omnivore-anchor-idx="561" tabindex="0"><code data-omnivore-anchor-idx="562" class="hljs language-routeros language-bash">bindkey -v
  <span data-omnivore-anchor-idx="563" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="564" class="hljs-attribute">KEYTIMEOUT</span>=1
  </code></pre></div><p data-omnivore-anchor-idx="565">You can now switch between INSERT and NORMAL mode (called also COMMAND mode) with the <code data-omnivore-anchor-idx="566" class="hljs language-ebnf"><span data-omnivore-anchor-idx="567" class="hljs-attribute">ESC</span></code> key, and use the familiar Vim keystrokes to edit what you’re typing in your shell prompt. I write the different modes in uppercase here for clarity, but it doesn’t have to be.</p><p data-omnivore-anchor-idx="568">The second line <code data-omnivore-anchor-idx="569" class="hljs language-routeros language-angelscript"><span data-omnivore-anchor-idx="570" class="hljs-builtin-name">export</span> <span data-omnivore-anchor-idx="571" class="hljs-attribute">KEYTIMEOUT</span>=1</code> makes the switch between modes quicker.</p><h3 data-omnivore-anchor-idx="572" id="changing-cursor">Changing Cursor</h3><p data-omnivore-anchor-idx="573">A visual indicator to show the current mode (NORMAL or INSERT) could be nice. In Vim, my cursor is a beam <code data-omnivore-anchor-idx="574" class="hljs language-1c"><span data-omnivore-anchor-idx="575" class="hljs-string">|</span></code> when I’m in INSERT mode, and a block <code data-omnivore-anchor-idx="576" class="hljs language-undefined">█</code> when I’m in NORMAL mode. I wanted the same for Zsh.</p><p data-omnivore-anchor-idx="577">You can add the following in your <code data-omnivore-anchor-idx="578" class="hljs language-ebnf"><span data-omnivore-anchor-idx="579" class="hljs-attribute">zshrc</span></code>, or autoload it from a file, <a data-omnivore-anchor-idx="580" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/plugins/cursor_mode" target="_blank" rel="noopener">as I did</a>.</p><div data-omnivore-anchor-idx="581"><pre data-omnivore-anchor-idx="582" tabindex="0"><code data-omnivore-anchor-idx="583" class="hljs language-bash language-perl"><span data-omnivore-anchor-idx="584" class="hljs-function"><span data-omnivore-anchor-idx="585" class="hljs-title">cursor_mode</span></span>() {
      <span data-omnivore-anchor-idx="586" class="hljs-comment"># See https://ttssh2.osdn.jp/manual/4/en/usage/tips/vim.html for cursor shapes</span>
      cursor_block=<span data-omnivore-anchor-idx="587" class="hljs-string">'\e[2 q'</span>
      cursor_beam=<span data-omnivore-anchor-idx="588" class="hljs-string">'\e[6 q'</span>

      <span data-omnivore-anchor-idx="589" class="hljs-keyword">function</span> <span data-omnivore-anchor-idx="590" class="hljs-built_in">zle</span>-keymap-select {
          <span data-omnivore-anchor-idx="591" class="hljs-keyword">if</span> [[ <span data-omnivore-anchor-idx="592" class="hljs-variable">${KEYMAP}</span> == vicmd ]] ||
              [[ <span data-omnivore-anchor-idx="593" class="hljs-variable">$1</span> = <span data-omnivore-anchor-idx="594" class="hljs-string">'block'</span> ]]; <span data-omnivore-anchor-idx="595" class="hljs-keyword">then</span>
              <span data-omnivore-anchor-idx="596" class="hljs-built_in">echo</span> -ne <span data-omnivore-anchor-idx="597" class="hljs-variable">$cursor_block</span>
          <span data-omnivore-anchor-idx="598" class="hljs-keyword">elif</span> [[ <span data-omnivore-anchor-idx="599" class="hljs-variable">${KEYMAP}</span> == main ]] ||
              [[ <span data-omnivore-anchor-idx="600" class="hljs-variable">${KEYMAP}</span> == viins ]] ||
              [[ <span data-omnivore-anchor-idx="601" class="hljs-variable">${KEYMAP}</span> = <span data-omnivore-anchor-idx="602" class="hljs-string">''</span> ]] ||
              [[ <span data-omnivore-anchor-idx="603" class="hljs-variable">$1</span> = <span data-omnivore-anchor-idx="604" class="hljs-string">'beam'</span> ]]; <span data-omnivore-anchor-idx="605" class="hljs-keyword">then</span>
              <span data-omnivore-anchor-idx="606" class="hljs-built_in">echo</span> -ne <span data-omnivore-anchor-idx="607" class="hljs-variable">$cursor_beam</span>
          <span data-omnivore-anchor-idx="608" class="hljs-keyword">fi</span>
      }

      <span data-omnivore-anchor-idx="609" class="hljs-built_in">zle</span>-line-<span data-omnivore-anchor-idx="610" class="hljs-function"><span data-omnivore-anchor-idx="611" class="hljs-title">init</span></span>() {
          <span data-omnivore-anchor-idx="612" class="hljs-built_in">echo</span> -ne <span data-omnivore-anchor-idx="613" class="hljs-variable">$cursor_beam</span>
      }

      <span data-omnivore-anchor-idx="614" class="hljs-built_in">zle</span> -N <span data-omnivore-anchor-idx="615" class="hljs-built_in">zle</span>-keymap-select
      <span data-omnivore-anchor-idx="616" class="hljs-built_in">zle</span> -N <span data-omnivore-anchor-idx="617" class="hljs-built_in">zle</span>-line-init
  }

  cursor_mode
  </code></pre></div><p data-omnivore-anchor-idx="618">You can now speak about beams and blocks with passion and verve.</p><h3 data-omnivore-anchor-idx="619" id="vim-mapping-for-completion">Vim Mapping For Completion</h3><p data-omnivore-anchor-idx="620">To give Zsh more of a Vim taste, we can set up the keys <code data-omnivore-anchor-idx="621" class="hljs language-ebnf"><span data-omnivore-anchor-idx="622" class="hljs-attribute">hjkl</span></code> to navigate the completion menu.</p><p data-omnivore-anchor-idx="623">First, add the following to your <code data-omnivore-anchor-idx="624" class="hljs language-ebnf"><span data-omnivore-anchor-idx="625" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="626"><pre data-omnivore-anchor-idx="627" tabindex="0"><code data-omnivore-anchor-idx="628" class="hljs language-vim language-bash">zmodload zsh/complist
  bindkey -M menuselect <span data-omnivore-anchor-idx="629" class="hljs-string">'h'</span> <span data-omnivore-anchor-idx="630" class="hljs-keyword">vi</span>-backward-char
  bindkey -M menuselect <span data-omnivore-anchor-idx="631" class="hljs-string">'k'</span> <span data-omnivore-anchor-idx="632" class="hljs-keyword">vi</span>-<span data-omnivore-anchor-idx="633" class="hljs-keyword">up</span>-<span data-omnivore-anchor-idx="634" class="hljs-built_in">line</span>-<span data-omnivore-anchor-idx="635" class="hljs-built_in">or</span>-<span data-omnivore-anchor-idx="636" class="hljs-keyword">history</span>
  bindkey -M menuselect <span data-omnivore-anchor-idx="637" class="hljs-string">'l'</span> <span data-omnivore-anchor-idx="638" class="hljs-keyword">vi</span>-forward-char
  bindkey -M menuselect <span data-omnivore-anchor-idx="639" class="hljs-string">'j'</span> <span data-omnivore-anchor-idx="640" class="hljs-keyword">vi</span>-down-<span data-omnivore-anchor-idx="641" class="hljs-built_in">line</span>-<span data-omnivore-anchor-idx="642" class="hljs-built_in">or</span>-<span data-omnivore-anchor-idx="643" class="hljs-keyword">history</span>
  </code></pre></div><p data-omnivore-anchor-idx="644">We load here the Zsh module <code data-omnivore-anchor-idx="645" class="hljs language-ebnf"><span data-omnivore-anchor-idx="646" class="hljs-attribute">complist</span></code>. Modules have functionalities which are not part of the Zsh’s core, but they can be loaded on demand. <a data-omnivore-anchor-idx="647" href="http://zsh.sourceforge.net/Doc/Release/Zsh-Modules.html" target="_blank" rel="noopener">Many different modules are available</a> for your needs.</p><p data-omnivore-anchor-idx="648">Here, the module <code data-omnivore-anchor-idx="649" class="hljs language-ebnf"><span data-omnivore-anchor-idx="650" class="hljs-attribute">complist</span></code> give you access to the keymap <code data-omnivore-anchor-idx="651" class="hljs language-ebnf"><span data-omnivore-anchor-idx="652" class="hljs-attribute">menuselect</span></code>, to customize the menu selection during completion, including how to select what you want.</p><p data-omnivore-anchor-idx="653">In general, the command <code data-omnivore-anchor-idx="654" class="hljs language-armasm language-bash"><span data-omnivore-anchor-idx="655" class="hljs-keyword">bindkey </span>-M</code> bind a key to a specific <em data-omnivore-anchor-idx="656">keymap</em>. A keymap is a set of keystrokes bind to specific Zsh functions. In this case, the keymap <code data-omnivore-anchor-idx="657" class="hljs language-ebnf"><span data-omnivore-anchor-idx="658" class="hljs-attribute">menuselect</span></code> bind keystrokes with selecting something in a list.</p><p data-omnivore-anchor-idx="659">To list all the keymaps available (depending on the modules you’ve loaded), you can run in your shell <code data-omnivore-anchor-idx="660" class="hljs language-armasm language-bash"><span data-omnivore-anchor-idx="661" class="hljs-keyword">bindkey </span>-l</code> (for <code data-omnivore-anchor-idx="662" class="hljs language-ebnf"><span data-omnivore-anchor-idx="663" class="hljs-attribute">l</span></code>ist). You can also <a data-omnivore-anchor-idx="664" href="http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Keymaps" target="_blank" rel="noopener">find the default ones here</a>.</p><p data-omnivore-anchor-idx="665">Last thing: you should always load the module <code data-omnivore-anchor-idx="666" class="hljs language-undefined">zsh/complist</code> <em data-omnivore-anchor-idx="667">before</em> autoloading <code data-omnivore-anchor-idx="668" class="hljs language-ebnf"><span data-omnivore-anchor-idx="669" class="hljs-attribute">compinit</span></code>.</p><h3 data-omnivore-anchor-idx="670" id="editing-command-lines-in-vim">Editing Command Lines In Vim</h3><p data-omnivore-anchor-idx="671">Good news: you can use your favorite editor to edit the commands you’re typing in your prompt! Let’s add these lines in your <code data-omnivore-anchor-idx="672" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="673" class="hljs-title">.zshrc</span></code> to do so:</p><div data-omnivore-anchor-idx="674"><pre data-omnivore-anchor-idx="675" tabindex="0"><code data-omnivore-anchor-idx="676" class="hljs language-vim language-gauss">autoload -Uz <span data-omnivore-anchor-idx="677" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="678" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="679" class="hljs-built_in">line</span>
  zle -<span data-omnivore-anchor-idx="680" class="hljs-keyword">N</span> <span data-omnivore-anchor-idx="681" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="682" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="683" class="hljs-built_in">line</span>
  bindkey -M vicmd v <span data-omnivore-anchor-idx="684" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="685" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="686" class="hljs-built_in">line</span>
  </code></pre></div><p data-omnivore-anchor-idx="687">Here, we autoload <code data-omnivore-anchor-idx="688" class="hljs language-vim language-gauss"><span data-omnivore-anchor-idx="689" class="hljs-keyword">edit</span>-<span data-omnivore-anchor-idx="690" class="hljs-keyword">command</span>-<span data-omnivore-anchor-idx="691" class="hljs-built_in">line</span></code>, a function from the module <a data-omnivore-anchor-idx="692" href="https://linux.die.net/man/1/zshcontrib" target="_blank" rel="noopener">zshcontrib</a>, which includes many contributions from Zsh users. This specific function let you edit a command line in your visual editor, defined by the environment variable <code data-omnivore-anchor-idx="693" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="694" class="hljs-meta"><span data-omnivore-anchor-idx="695" class="hljs-meta-keyword">$VISUAL</span></span></code> (or <code data-omnivore-anchor-idx="696" class="hljs language-gams language-autoit"><span data-omnivore-anchor-idx="697" class="hljs-meta"><span data-omnivore-anchor-idx="698" class="hljs-meta-keyword">$EDITOR</span></span></code>). Great! That’s what we wanted.</p><p data-omnivore-anchor-idx="699">We already saw <code data-omnivore-anchor-idx="700" class="hljs language-armasm language-bash"><span data-omnivore-anchor-idx="701" class="hljs-keyword">bindkey </span>-M</code>. Using the keymap <code data-omnivore-anchor-idx="702" class="hljs language-ebnf"><span data-omnivore-anchor-idx="703" class="hljs-attribute">vicmd</span></code>, we can bind commands to some NORMAL mode keystrokes. It means that, when you’re in NORMAL mode, you can hit <code data-omnivore-anchor-idx="704" class="hljs language-ebnf"><span data-omnivore-anchor-idx="705" class="hljs-attribute">v</span></code> to directly edit your command in your editor.</p><h3 data-omnivore-anchor-idx="706" id="adding-text-objects">Adding Text Objects</h3><p data-omnivore-anchor-idx="707">If you use the Vi-mode of Zsh for a while, you’ll notice that there are no text objects for quotes or brackets: impossible to do something like <code data-omnivore-anchor-idx="708" class="hljs language-1c language-vim">da<span data-omnivore-anchor-idx="709" class="hljs-string">"</span></code> (to delete a quoted substring) or <code data-omnivore-anchor-idx="710" class="hljs language-isbl language-stata"><span data-omnivore-anchor-idx="711" class="hljs-function"><span data-omnivore-anchor-idx="712" class="hljs-title">ci</span>(</span></code> (to change inside parenthesis). Zsh supports these, you just need to generate and bind them to specific Zsh widgets:</p><div data-omnivore-anchor-idx="713"><pre data-omnivore-anchor-idx="714" tabindex="0"><code data-omnivore-anchor-idx="715" class="hljs language-bash language-properties"><span data-omnivore-anchor-idx="716" class="hljs-built_in">autoload</span> -Uz select-bracketed select-quoted
  <span data-omnivore-anchor-idx="717" class="hljs-built_in">zle</span> -N select-quoted
  <span data-omnivore-anchor-idx="718" class="hljs-built_in">zle</span> -N select-bracketed
  <span data-omnivore-anchor-idx="719" class="hljs-keyword">for</span> km <span data-omnivore-anchor-idx="720" class="hljs-keyword">in</span> viopp visual; <span data-omnivore-anchor-idx="721" class="hljs-keyword">do</span>
    <span data-omnivore-anchor-idx="722" class="hljs-built_in">bindkey</span> -M <span data-omnivore-anchor-idx="723" class="hljs-variable">$km</span> -- <span data-omnivore-anchor-idx="724" class="hljs-string">'-'</span> vi-up-line-or-history
    <span data-omnivore-anchor-idx="725" class="hljs-keyword">for</span> c <span data-omnivore-anchor-idx="726" class="hljs-keyword">in</span> {a,i}<span data-omnivore-anchor-idx="727" class="hljs-variable">${(s..)^:-\'\"\`\|,./:;=+@}</span>; <span data-omnivore-anchor-idx="728" class="hljs-keyword">do</span>
      <span data-omnivore-anchor-idx="729" class="hljs-built_in">bindkey</span> -M <span data-omnivore-anchor-idx="730" class="hljs-variable">$km</span> <span data-omnivore-anchor-idx="731" class="hljs-variable">$c</span> select-quoted
    <span data-omnivore-anchor-idx="732" class="hljs-keyword">done</span>
    <span data-omnivore-anchor-idx="733" class="hljs-keyword">for</span> c <span data-omnivore-anchor-idx="734" class="hljs-keyword">in</span> {a,i}<span data-omnivore-anchor-idx="735" class="hljs-variable">${(s..)^:-'()[]{}</span>&lt;&gt;bB<span data-omnivore-anchor-idx="736" class="hljs-string">'}; do
      bindkey -M $km $c select-bracketed
    done
  done
  </span></code></pre></div><p data-omnivore-anchor-idx="737">If you want to know more about Zsh widgets, I’ve <a data-omnivore-anchor-idx="738" href="https://thevaluable.dev/zsh-line-editor-configuration-mouseless/">written another article about that</a>, where I also explain the code above.</p><h3 data-omnivore-anchor-idx="739" id="surrounding">Surrounding</h3><p data-omnivore-anchor-idx="740">Zsh also allows us to mimic the famous <a data-omnivore-anchor-idx="741" href="https://github.com/tpope/vim-surround" target="_blank" rel="noopener">Tim Pope’s surround plugin</a>. Just add the following to your <code data-omnivore-anchor-idx="742" class="hljs language-ebnf"><span data-omnivore-anchor-idx="743" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="744"><pre data-omnivore-anchor-idx="745" tabindex="0"><code data-omnivore-anchor-idx="746" class="hljs language-smali language-dsconfig">autoload -Uz surround
  zle -N delete-surround surround
  zle -N<span data-omnivore-anchor-idx="747" class="hljs-built_in"> add-surround </span>surround
  zle -N change-surround surround
  bindkey -M vicmd cs change-surround
  bindkey -M vicmd ds delete-surround
  bindkey -M vicmd ys<span data-omnivore-anchor-idx="748" class="hljs-built_in"> add-surround
  </span>bindkey -M visual S<span data-omnivore-anchor-idx="749" class="hljs-built_in"> add-surround
  </span></code></pre></div><p data-omnivore-anchor-idx="750">You can then use <code data-omnivore-anchor-idx="751" class="hljs language-ebnf language-stata"><span data-omnivore-anchor-idx="752" class="hljs-attribute">cs</span></code> (change surrounding), <code data-omnivore-anchor-idx="753" class="hljs language-ebnf language-stata"><span data-omnivore-anchor-idx="754" class="hljs-attribute">ds</span></code> (delete surrounding), <code data-omnivore-anchor-idx="755" class="hljs language-ebnf"><span data-omnivore-anchor-idx="756" class="hljs-attribute">ys</span></code> (add surrounding) in Zsh’s NORMAL mode.</p><h2 data-omnivore-anchor-idx="757" id="zsh-plugins">Zsh Plugins</h2><p data-omnivore-anchor-idx="758">The term “plugin”, as I use it, has nothing official. People often speak about Zsh plugins as external pieces of configuration you can add to your own.</p><p data-omnivore-anchor-idx="759">There are many of these plugins available for Zsh. Many of them are part of Zsh frameworks.</p><h3 data-omnivore-anchor-idx="760" id="zsh-completions">Zsh Completions</h3><p data-omnivore-anchor-idx="761">By default, Zsh can complete already many popular CLIs like <code data-omnivore-anchor-idx="762" class="hljs language-bash language-dos"><span data-omnivore-anchor-idx="763" class="hljs-built_in">cd</span></code>, <code data-omnivore-anchor-idx="764" class="hljs language-avrasm language-ebnf"><span data-omnivore-anchor-idx="765" class="hljs-keyword">cp</span></code>, <code data-omnivore-anchor-idx="766" class="hljs language-ebnf"><span data-omnivore-anchor-idx="767" class="hljs-attribute">git</span></code>, and so on.</p><p data-omnivore-anchor-idx="768">The plugin <a data-omnivore-anchor-idx="769" href="https://github.com/zsh-users/zsh-completions" target="_blank" rel="noopener">zsh-completions</a> add even more completions. The <a data-omnivore-anchor-idx="770" href="https://github.com/zsh-users/zsh-completions/tree/master/src" target="_blank" rel="noopener">list of the newly supported CLIs is here</a></p><p data-omnivore-anchor-idx="771">If you don’t use any of the program listed, you don’t need this plugin.</p><p data-omnivore-anchor-idx="772">I added <code data-omnivore-anchor-idx="773" class="hljs language-ebnf"><span data-omnivore-anchor-idx="774" class="hljs-attribute">zsh-completion</span></code> as a <a data-omnivore-anchor-idx="775" href="https://github.com/Phantas0s/.dotfiles/blob/master/.gitmodules" target="_blank" rel="noopener">git submodule in my dotfiles</a>. Then, you can automatically add every completion to your <code data-omnivore-anchor-idx="776" class="hljs language-ebnf"><span data-omnivore-anchor-idx="777" class="hljs-attribute">fpath</span></code>, in your zshrc:</p><div data-omnivore-anchor-idx="778"><pre data-omnivore-anchor-idx="779" tabindex="0"><code data-omnivore-anchor-idx="780" class="hljs language-elixir language-ruby">fpath=(<span data-omnivore-anchor-idx="781" class="hljs-regexp">/path/to</span><span data-omnivore-anchor-idx="782" class="hljs-regexp">/my/zsh</span><span data-omnivore-anchor-idx="783" class="hljs-regexp">/plugins/zsh</span>-completions/src <span data-omnivore-anchor-idx="784" class="hljs-variable">$fpath</span>)
  </code></pre></div><p data-omnivore-anchor-idx="785">You don’t need to load every completion file, one by one. If you look at the beginning of one of these files, you’ll see <code data-omnivore-anchor-idx="786" class="hljs language-ebnf"><span data-omnivore-anchor-idx="787" class="hljs-attribute">compdef</span></code>. It’s a function from Zsh which load automagically the completion when it’s needed. The completion file itself only needs to be included in your <code data-omnivore-anchor-idx="788" class="hljs language-ebnf"><span data-omnivore-anchor-idx="789" class="hljs-attribute">fpath</span></code>.</p><p data-omnivore-anchor-idx="790">You can also cherry-pick the specific completions you want.</p><h3 data-omnivore-anchor-idx="791" id="zsh-syntax-highlighting">Zsh Syntax Highlighting</h3><p data-omnivore-anchor-idx="792">What about syntax highlighting in Zsh? That’s what <a data-omnivore-anchor-idx="793" href="https://github.com/zsh-users/zsh-syntax-highlighting" target="_blank" rel="noopener">zsh-syntax-highlighting</a> is about.</p><p data-omnivore-anchor-idx="794">You can source it directly:</p><div data-omnivore-anchor-idx="795"><pre data-omnivore-anchor-idx="796" tabindex="0"><code data-omnivore-anchor-idx="797" class="hljs language-vim language-dts"><span data-omnivore-anchor-idx="798" class="hljs-keyword">source</span> /path/<span data-omnivore-anchor-idx="799" class="hljs-keyword">to</span>/my/zsh/plugins/zsh-<span data-omnivore-anchor-idx="800" class="hljs-keyword">syntax</span>-highlighting/zsh-<span data-omnivore-anchor-idx="801" class="hljs-keyword">syntax</span>-highlighting.zsh
  </code></pre></div><p data-omnivore-anchor-idx="802">There is one minor downside however: it seems to be currently incompatible with the surround widget we’ve seen above. If you want to use both, you need to use the branch <a data-omnivore-anchor-idx="803" href="https://github.com/zsh-users/zsh-syntax-highlighting/tree/feature/redrawhook" target="_blank" rel="noopener">feature/redrawhook</a>.</p><p data-omnivore-anchor-idx="804">You should source this plugin at the bottom of your <code data-omnivore-anchor-idx="805" class="hljs language-ebnf"><span data-omnivore-anchor-idx="806" class="hljs-attribute">zshrc</span></code>. Everything loaded before will then be able to use syntax highlighting if needed.</p><h3 data-omnivore-anchor-idx="807" id="jumping-to-a-parent-directory-easily">Jumping To A Parent Directory Easily</h3><p data-omnivore-anchor-idx="808">Do you like to type <code data-omnivore-anchor-idx="809" class="hljs language-routeros language-gams">cd <span data-omnivore-anchor-idx="810" class="hljs-built_in">..</span>/<span data-omnivore-anchor-idx="811" class="hljs-built_in">..</span>/<span data-omnivore-anchor-idx="812" class="hljs-built_in">..</span></code> to come back to the great-grand-parent of the current folder?</p><p data-omnivore-anchor-idx="813">Me neither.</p><p data-omnivore-anchor-idx="814">It’s where <a data-omnivore-anchor-idx="815" href="https://github.com/Tarrasch/zsh-bd" target="_blank" rel="noopener">bd</a> can help you. Imagine that you’re in the folder <code data-omnivore-anchor-idx="816" class="hljs language-awk language-crystal">~<span data-omnivore-anchor-idx="817" class="hljs-regexp">/a/</span>b<span data-omnivore-anchor-idx="818" class="hljs-regexp">/c/</span>d</code>. You can jump directly to <code data-omnivore-anchor-idx="819" class="hljs language-ebnf language-livecodeserver"><span data-omnivore-anchor-idx="820" class="hljs-attribute">a</span></code> with the command <code data-omnivore-anchor-idx="821" class="hljs language-armasm language-ebnf"><span data-omnivore-anchor-idx="822" class="hljs-keyword">bd </span>a</code>.</p><p data-omnivore-anchor-idx="823">The Zsh completion is even included. Awesomeness!</p><p data-omnivore-anchor-idx="824">To use it, you need to source the file <a data-omnivore-anchor-idx="825" href="https://github.com/Tarrasch/zsh-bd/blob/master/bd.zsh" target="_blank" rel="noopener">bd.zsh</a>.</p><h2 data-omnivore-anchor-idx="826" id="custom-scripts">Custom Scripts</h2><p data-omnivore-anchor-idx="827">Using a shell allows you to automate many parts of your workflow with shell scripts. That’s a huge benefit you should take advantage of.</p><p data-omnivore-anchor-idx="828">I keep most of <a data-omnivore-anchor-idx="829" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/scripts.zsh" target="_blank" rel="noopener">my scripts in one file</a> and I <a data-omnivore-anchor-idx="830" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/README.md" target="_blank" rel="noopener">document them</a> (roughly) for me to remember what’s in there, and for others to get inspired.</p><p data-omnivore-anchor-idx="831">I source the functions in my <code data-omnivore-anchor-idx="832" class="hljs language-asciidoc language-css"><span data-omnivore-anchor-idx="833" class="hljs-title">.zshrc</span></code>, but you could autoload them too.</p><p data-omnivore-anchor-idx="834">While working, ask yourself what tasks you do again and again, to automate them as much as you can. This is the real power of the shell, and it will make your whole workflow more fun.</p><h2 data-omnivore-anchor-idx="835" id="external-programs">External Programs</h2><p data-omnivore-anchor-idx="836">A shell without CLIs would be useless. Here are my personal favorites to expand Zsh functionalities.</p><h3 data-omnivore-anchor-idx="837" id="multiplex-your-zsh-with-tmux">Multiplex Your Zsh With tmux</h3><p data-omnivore-anchor-idx="838">I’ve already <a data-omnivore-anchor-idx="839" href="https://thevaluable.dev/tmux-boost-productivity-terminal/">written about tmux here</a>. It’s a terminal multiplexer with a tonne of functionalities: you can split your terminal in many windows or panes, synchronize them, and keep your sessions alive even without terminal. You can even extend it with plugins helping you automating your whole shell workflow.</p><h3 data-omnivore-anchor-idx="840" id="fuzzy-search-with-fzf">Fuzzy Search With fzf</h3><p data-omnivore-anchor-idx="841">The fuzzy finder <code data-omnivore-anchor-idx="842" class="hljs language-ebnf"><span data-omnivore-anchor-idx="843" class="hljs-attribute">fzf</span></code> is a fast and powerful tool. You can use it to search anything you want, like a file, an entry in your command line history, or a specific git commit message.</p><p data-omnivore-anchor-idx="844">I wrote (or copied and pasted) a bunch of <a data-omnivore-anchor-idx="845" href="https://github.com/Phantas0s/.dotfiles/blob/master/zsh/scripts_fzf.zsh" target="_blank" rel="noopener">scripts using zsh</a> too, to search through git logs or <code data-omnivore-anchor-idx="846" class="hljs language-ebnf"><span data-omnivore-anchor-idx="847" class="hljs-attribute">tmuxp</span></code> projects.</p><p data-omnivore-anchor-idx="848">There are different ways to install <code data-omnivore-anchor-idx="849" class="hljs language-ebnf"><span data-omnivore-anchor-idx="850" class="hljs-attribute">fzf</span></code>. You’ll need first the executable. Then, I would recommend sourcing the files:</p><ul data-omnivore-anchor-idx="851"><li data-omnivore-anchor-idx="852"><code data-omnivore-anchor-idx="853" class="hljs language-css language-gauss"><span data-omnivore-anchor-idx="854" class="hljs-selector-tag">key-bindings</span><span data-omnivore-anchor-idx="855" class="hljs-selector-class">.zsh</span></code>, which will include some practical keystrokes like <code data-omnivore-anchor-idx="856" class="hljs language-ebnf"><span data-omnivore-anchor-idx="857" class="hljs-attribute">Ctrl-h</span></code> or <code data-omnivore-anchor-idx="858" class="hljs language-ebnf language-excel"><span data-omnivore-anchor-idx="859" class="hljs-attribute">Ctrl-t</span></code></li><li data-omnivore-anchor-idx="860"><code data-omnivore-anchor-idx="861" class="hljs language-css"><span data-omnivore-anchor-idx="862" class="hljs-selector-tag">completion</span><span data-omnivore-anchor-idx="863" class="hljs-selector-class">.zsh</span></code>, for <code data-omnivore-anchor-idx="864" class="hljs language-ebnf"><span data-omnivore-anchor-idx="865" class="hljs-attribute">fzf</span></code> completion.</li></ul><p data-omnivore-anchor-idx="866">If you use Arch Linux, you’ll need to install the package <code data-omnivore-anchor-idx="867" class="hljs language-ebnf"><span data-omnivore-anchor-idx="868" class="hljs-attribute">fzf</span></code> and simply source these two files in your <code data-omnivore-anchor-idx="869" class="hljs language-ebnf"><span data-omnivore-anchor-idx="870" class="hljs-attribute">zshrc</span></code>:</p><div data-omnivore-anchor-idx="871"><pre data-omnivore-anchor-idx="872" tabindex="0"><code data-omnivore-anchor-idx="873" class="hljs language-gradle language-awk"><span data-omnivore-anchor-idx="874" class="hljs-keyword">source</span> <span data-omnivore-anchor-idx="875" class="hljs-regexp">/usr/</span>share<span data-omnivore-anchor-idx="876" class="hljs-regexp">/fzf/</span>completion.zsh
  <span data-omnivore-anchor-idx="877" class="hljs-keyword">source</span> <span data-omnivore-anchor-idx="878" class="hljs-regexp">/usr/</span>share<span data-omnivore-anchor-idx="879" class="hljs-regexp">/fzf/</span>key-bindings.zsh
  </code></pre></div><p data-omnivore-anchor-idx="880">Otherwise, you’ll need to follow the installation process from fzf’s README file.</p><h2 data-omnivore-anchor-idx="881" id="the-z-shell-is-now-yours">The Z-Shell Is Now Yours</h2><p data-omnivore-anchor-idx="882">You should now have a clean and lean Zsh configuration, and you should understand enough of it to customize it.</p><p data-omnivore-anchor-idx="883">What did we learn with this article?</p><ul data-omnivore-anchor-idx="884"><li data-omnivore-anchor-idx="885">Zsh reads its configuration files in a precise order.</li><li data-omnivore-anchor-idx="886">You can set (or unset) many Zsh options depending on your needs.</li><li data-omnivore-anchor-idx="887">The completion system of Zsh is one of its best feature.</li><li data-omnivore-anchor-idx="888">Zsh directory stack allow you to jump easily in directories you’ve already visited.</li><li data-omnivore-anchor-idx="889">If you like Vim, Zsh allows you to use keystrokes from the Vim world. You can even edit your commands directly in Vim.</li><li data-omnivore-anchor-idx="890">External plugins can be found on The Internet, to improve even further the Zsh experience.</li><li data-omnivore-anchor-idx="891">You should go crazy on shell scripting, to automate your workflow as much as you can.</li><li data-omnivore-anchor-idx="892">External programs can enhance your experience with the shell, like <code data-omnivore-anchor-idx="893" class="hljs language-ebnf"><span data-omnivore-anchor-idx="894" class="hljs-attribute">tmux</span></code> or <code data-omnivore-anchor-idx="895" class="hljs language-ebnf"><span data-omnivore-anchor-idx="896" class="hljs-attribute">fzf</span></code>.</li></ul><p data-omnivore-anchor-idx="897">All your colleagues will be jealous. Guaranteed.</p></section></article></main></div></DIV></DIV>

metadata:
  {"date_added":"2024-02-15 10:01:52","id":"191e19a9-a07e-479a-82bc-52f90227746c","status":"ARCHIVED","tags":["read-later"],"title":"Configuring Zsh Without Dependencies\n","url_omnivore":"https://omnivore.app/me/https-thevaluable-dev-zsh-install-configure-mouseless-18dacdbdb29\n","url_original":"https://thevaluable.dev/zsh-install-configure-mouseless/\n"}


path: Omnivore/2024-02-15 - React Optimization Techniques to Help You Write More Performant Code.md
sortable_path: Omnivore2024-02-15 - React Optimization Techniques to Help You Write More Performant Code.md
title: React Optimization Techniques to Help You Write More Performant Code
lead: # React Optimization Techniques to Help You Write More Performant Code
checksum: f6a3434285fbacade21a7add6762b56f4c298bd8d451b04b6dc82f9ce7d12546
created: 2024-09-15 00:40:57.920332118+00:00
modified: 2024-09-22 22:40:13.395511052+00:00
body:
  # React Optimization Techniques to Help You Write More Performant Code

  ## Highlights

  List visualization, or windowing, involves rendering only the items currently visible on the screen. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#9fb355ed-811d-44e7-aa61-12c0b18db7e2) 

  ---

  Lazy loading allows you to defer or delay the loading of images until they are needed or visible to the user instead of loading all the images on page load. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#f9c0a23a-9d66-4a80-bb4c-a4f2806ed1aa) 

  ---

  Another approach is to use the [intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection%5FObserver%5FAPI), which is a web API that allows you to detect when an element enters or exists the viewport efficiently. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#a44706c9-5cce-4765-ac78-77efdca40a30) 

  ---

  Memoization in React is a technique used to optimize the performance of functional components by caching the results of expensive computations or function calls. It's particularly useful when dealing with computationally intensive or frequently called functions with the same input values, as it helps avoid redundant calculations and improves the overall efficiency of the application. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#051f1544-65d2-4eae-87c6-bb7f226c4fa7) 

  ---

  Below is an example on how to use the `React.memo` with a functional component:

  ```javascript
  import React from 'react';

  const Post = ({ signedIn, post }) => {
    console.log('Rendering Post');
    return (
      
        {post.title}
        {post.content}
        {signedIn && Edit Post}
      
    );
  };

  export default React.memo(Post);

  ```

  In the code above, `Post` (functional component) depends on the `signedIn` and `post` props. By wrapping it with `React.memo()`, React will only re-render the `Post` component if either `signedIn` or `post` changes. 

  > [!note]
  > You can memoize complete components to prevent re-renders if the props doesn't change, this use `React.memo()`, not the `useMemo()` hook

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#f0735f25-7bdf-4562-84cb-6f93f3a0b5a6) 

  ---

  The `useMemo()` hook optimizes performance by memoizing the result of a function call or an expensive computation. It caches the result and recalculates it only when the input values change. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#360c5717-251f-40d7-9dd8-24bccb26a59b) 

  ---

  The `useCallback()` hook in React is used to memoize a function instead of memoizing the function result. It is particularly useful when passing events as props to child components to prevent unnecessary re-renders. 

  `useCallback()` memoizes the function, ensuring it remains the same across re-renders as long as the dependencies haven't changed. 

  This is especially beneficial when passing functions as props to child components, preventing unnecessary re-renders. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#c01f0766-7401-44ae-9888-ee1da6d10ca7) 

  ---

  It's important to note that `useCallback` should be used sparingly and only for performance-critical parts of your application. Overusing `useCallback` can actually lead to worse performance due to the overhead of memoization itself. Always measure the performance impact before and after using `useCallback` to ensure it's having the desired effect. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#4292a582-b6cb-44e9-baff-3be6a127ed14) 

  ---

  Throttling in React is a technique used to limit the number of times a function or an event handler is invoked. It ensures that the function is called at a specified interval, preventing it from being executed too frequently. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#5e73fc50-7717-449d-a27f-f3bb87fd100f) 

  ---

  Debouncing, on the other hand, is also used to limit the number of times a function or an event handler is invoked. It ensures that the function is called only after a certain period of inactivity. Debouncing allows you to postpone the function call until the user has finished typing or a specific time has elapsed since the last event. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#2926578f-0a36-4acc-8c03-5d740981bb04) 

  ---

  // Debounce function to delay the searchAPI call const debounce = (func, delay) => {let timeoutId;return function (...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() \=> { func(...args); }, delay); }; }; 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#131255d3-029e-43c9-8c5e-0489e786e425) 

  ---

  Code splitting in React is a technique used to split a large JavaScript bundle into smaller, manageable chunks. It helps improve performance by loading only the necessary code for a specific part of an application rather than loading the entire bundle upfront. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#9f66f0bf-2185-499a-a4f1-591e5af60182) 

  ---

  In this example, `AsyncComponent` is a component that uses `lazy` and `Suspense` to perform code splitting. The `DynamicComponent` is dynamically imported using the import() syntax. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#aff44dd9-50f2-4ad2-8f93-d52e6ab0988d) 

  ---

  Web Workers serve as a solution to alleviate the burden on the main thread. They allow the execution of scripts in the background on a separate thread, distinct from the main JavaScript thread. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#16416227-0870-4503-a67d-76418223953a) 

  ---

  The `useTransition` hook in React plays a pivotal role in improving the performance of applications by allowing the marking of state updates as non-blocking transitions. This capability enables React to defer rendering for these updates, preventing UI blocking and enhancing overall responsiveness. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#0622edf4-fa98-4848-aeee-174fa1cbde22) 

  ---

  ## Original

  ![React Optimization Techniques to Help You Write More Performant Code](https://proxy-prod.omnivore-image-cache.app/2000x1333,sjdHfCDBz1P_bmEqEy9XMcgTCv4J-TX2BuonAzVYMZmk/https://www.freecodecamp.org/news/content/images/size/w2000/2024/02/pexels-howard-adams-575835--1-.jpg) 

  Performance optimization is a critical aspect of developing web applications. Users expect applications to load quickly and respond to their interactions smoothly. 

  In the React ecosystem, performance optimization techniques can significantly enhance the user experience by reducing load times and improving responsiveness.

  In this article, we will discuss eight effective techniques for optimizing the performance of your React application.

  ## Table of Contents

  1. [Why Performance Optimization is Important](#why-performance-optimization-is-important)
  2. [List visualization](#list-visualization)
  3. [Lazy Loading Images](#lazy-loading-images)
  4. [Memoization](#memoization)
  5. [Throttling and Debouncing Events](#throttling-and-debouncing-events)
  6. [Code Splitting](#code-splitting)
  7. [React Fragments](#react-fragments)
  8. [Web Workers](#web-workers)
  9. [UseTransition Hook](#usetransition-hook)
  10. [Conclusion](#conclusion)

  ## Why Performance Optimization is Important

  Optimizing the performance of your React application is crucial for several reasons:

  * **Better User Experience:** A slow-loading or laggy application can lead to a poor user experience, negatively impacting your business. Users expect fast and responsive interactions, and performance optimization helps deliver that.
  * **Improved SEO:** Search engines like Google consider page load times and overall performance when ranking websites. A well-optimized application will rank higher in search results, making it more visible to potential users.
  * **Reduced Bounce Rates:** If your application takes too long to load or respond, users will likely leave and never return. By optimizing performance, you can reduce bounce rates and increase engagement.
  * **Cost Savings** A performant application requires fewer resources (like servers and memory) to handle the same workload. This means lower hosting costs and reduced infrastructure needs.
  * **Competitive Advantage:** A fast and efficient application sets you apart from competitors whose applications may be slower or less optimized. According to research by [Portent](https://www.portent.com/blog/analytics/research-site-speed-hurting-everyones-revenue.htm), a website that loads within one second has a conversion rate five times higher than a site that takes ten seconds to load. Therefore, ensuring your React applications perform well is crucial for retaining users and maintaining a competitive edge.

  ## 8 React Performance Optimization Techniques

  Below are eight React performance optimization techniques you can use to speed up your applications.

  ### List visualization

  ==List visualization, or windowing, involves rendering only the items currently visible on the screen.== 

  When dealing with a large number of items in a list, rendering all the items at once can lead to slow performance and consume a significant amount of memory. List virtualization tackles this issue by rendering only a subset of the list items currently visible within the view, which conserves resources as the users scroll through the list.

  The virtualization technique dynamically replaces rendered items with new ones, keeping the visible portion of the list updated and responsive. It efficiently allows you to render large lists or tabular data by only rendering the visible portion, recycling components as needed, and optimizing scroll performance.

  There are different approaches to implementing list visualization in React, and one is using a popular library called [React Virtualized](https://www.npmjs.com/package/react-virtualized). 

  To install `react-virtualized`, you can use the following command:

  ```sql
  npm install react-virtualized --save
  ```

  After installing `react-virtualized`, you can import the required components and styles. Below is an example of how to use the `List` component to create a virtualized list:

  ```javascript
  import React from 'react';
  import { List } from 'react-virtualized';
  import 'react-virtualized/styles.css'; // Import styles

  // Your list data
  const list = Array(5000).fill().map((_, index) => ({
    id: index,
    name: `Item ${index}`
  }));

  // Function to render each row
  function rowRenderer({ index, key, style }) {
    return (
      <div key={key} style={style}>
        {list[index].name}
      </div>
    );
  }

  // Main component
  function MyVirtualizedList() {
    return (
      <List
        width={300}
        height={300}
        rowCount={list.length}
        rowHeight={20}
        rowRenderer={rowRenderer}
      />
    );
  }

  export default MyVirtualizedList;

  ```

  In this example, `List` is the main component provided by `react-virtualized`. The `rowRenderer` function defines how each row should be rendered. The `width`, `height`, `rowCount`, `rowHeight`, and `rowRenderer` props are essential for configuring the list's behavior and appearance. 

  React applications can handle massive amounts of data by leveraging list virtualization without sacrificing performance or user experience.

  ### Lazy Loading Images

  Similar to the list virtualization technique, lazy loading images prevents the creation of unnecessary DOM nodes, thereby boosting performance. Lazy loading allows you to defer or delay the loading of images until they are needed or visible to the user instead of loading all the images on page load.

  The concept behind lazy loading is to initiate the load of a placeholder or a small low-resolution version of the image, typically a small-sized thumbnail or a blurred placeholder. As the user scrolls or interacts with the page, the actual image is loaded dynamically, replacing the placeholder when the user enters the viewport or when it becomes visible.

  Lazy loading in React can be achieved using various libraries and techniques. One of the popular libraries is the [react-lazyload](https://www.npmjs.com/package/react-lazyload). 

  To install `react-lazyload`, you can use the following command:

  ```sql
  npm install --save react-lazyload
  ```

  Below is an example of a simple React component that uses `react-lazyload` to implement lazy loading for images:

  ```javascript
  import React from 'react';
  import LazyLoad from 'react-lazyload';

  const MyLazyLoadedImage = ({ src, alt }) => {
    return (
      <LazyLoad height={200} offset={100}>
        {/* The height and offset props control when the image should start loading */}
        <img src={src} alt={alt} />
      </LazyLoad>
    );
  };

  export default MyLazyLoadedImage;

  ```

  In this example, `MyLazyLoadedImage` uses the `LazyLoad` component from `react-lazyload`. The `height` prop specifies the height of the placeholder, and the `offset` prop determines how far below the viewport the placeholder should start loading.

  ==Another approach is to use the== ==[intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection%5FObserver%5FAPI)====, which is a web API that allows you to detect when an element enters or exists the viewport efficiently.== Here's how we can use the Intersection Observer API along with the `useEffect` hook in React:

  ```javascript
  import React, { useEffect, useRef } from 'react';

  const IntersectionLazyLoad = ({ src, alt }) => {
    const imageRef = useRef();

    useEffect(() => {
      const options = {
        root: null, // Use the viewport as the root
        rootMargin: '0px', // No margin around the root
        threshold: 0.5, // 50% of the image should be visible
      };

      const observer = new IntersectionObserver(handleIntersection, options);

      if (imageRef.current) {
        observer.observe(imageRef.current);
      }

      return () => {
        // Cleanup the observer when the component is unmounted
        observer.disconnect();
      };
    }, []);

    const handleIntersection = (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          // Load the image when it becomes visible
          imageRef.current.src = src;
          imageRef.current.alt = alt;
        }
      });
    };

    return <img ref={imageRef} style={{ height: '200px' }} alt="Placeholder" />;
  };

  export default IntersectionLazyLoad;

  ```

  In this example, `IntersectionLazyLoad` uses the Intersection Observer API to determine when the image becomes visible in the viewport. 

  By utilizing this API along with React `useEffect` hook, you can implement your custom lazy loading solution for images in React.

  ### Memoization

  Memoization in React is a technique used to optimize the performance of functional components by caching the results of expensive computations or function calls. It's particularly useful when dealing with computationally intensive or frequently called functions with the same input values, as it helps avoid redundant calculations and improves the overall efficiency of the application.

  In React, there are three techniques for memoization: `React.memo()`, `useMemo(),` and `useCallback().` Let's delve into the details for each:

  #### How to use `React.memo()`

  This higher-order component wraps purely functional components to prevent re-rendering if the received props remain unchanged.

  By using `React.memo()`, the rendering result is cached based on props. If the props haven't changed since the last render, React reuses the previously rendered result instead of redoing the rendering process. This saves time and resources.

  ==Below is an example on how to use the== `==React====.====memo==` ==with a functional component:==

  ```javascript
  import React from 'react';

  const Post = ({ signedIn, post }) => {
    console.log('Rendering Post');
    return (
      <div>
        <h2>{post.title}</h2>
        <p>{post.content}</p>
        {signedIn && <button>Edit Post</button>}
      </div>
    );
  };

  export default React.memo(Post);

  ```

  ==In the code above,== `==Post==` ==(functional component) depends on the== `==signedIn==` ==and== `==post==` ==props. By wrapping it with== `==React====.====memo====()==`==, React will only re-render the== `==Post==` ==component if either== `==signedIn==` ==or== `==post==` ==changes.==

  You can now use the memoized component like any other component in your application:

  ```javascript
  import React, { useState } from 'react';
  import Post from './Post';

  const App = () => {
    const [signedIn, setSignedIn] = useState(false);
    const post = { title: 'Hello World', content: 'Welcome to my blog!' };

    return (
      <div>
        <Post signedIn={signedIn} post={post} />
        <button onClick={() => setSignedIn(!signedIn)}>
          Toggle Signed In
        </button>
      </div>
    );
  };

  export default App;

  ```

  When you click the `Toggle Signed In` button, it will toggle the `signedIn` state. Since `Post` is wrapped with `React.memo()`, it will only re-render when the `signedIn` prop changes, thus saving rendering time and resources

  #### How to use `useMemo()`

  The `useMemo()` hook optimizes performance by memoizing the result of a function call or an expensive computation. It caches the result and recalculates it only when the input values change. Below is an example on how to use the `useMemo` hook in functional component:

  ```javascript
  import React, { useMemo } from 'react';

  function App() {
    const [count, setCount] = React.useState(0);
    const [otherState, setOtherState] = React.useState('');

    const expensiveComputation = (num) => {
      let i =  0;
      while (i <  1000000000) i++;
      return num * num;
    };

    const memoizedValue = useMemo(() => expensiveComputation(count), [count]);

    return (
      <div>
        <p>Count: {count}</p>
        <p>Square: {memoizedValue}</p>
        <button onClick={() => setCount(count +  1)}>Increase Count</button>
        <input type="text" onChange={(e) => setOtherState(e.target.value)} />
      </div>
    );
  }

  export default App;

  ```

  In the code above, the `expensiveComputation` function simulates a resource-intensive operation, like squaring a number. 

  The `useMemo` hook is utilized to cache the result of this computation. The memoized value, stored in `memoizedValue`, is only recalculated when the `count` state changes, as `count` is specified as a dependency in the `useMemo` dependency array. Consequently, clicking the `Increase Count` button increments the `count` state, triggering a recalculation of the memoized value. 

  Conversely, changing the `otherState` via the input field does not prompt a recalculation, as `otherState` is not included in the `useMemo` dependency array.

  #### How to use `useCallback()`

  The `useCallback()` hook in React is used to memoize a function instead of memoizing the function result. It is particularly useful when passing events as props to child components to prevent unnecessary re-renders. 

  `useCallback()` memoizes the function, ensuring it remains the same across re-renders as long as the dependencies haven't changed. 

  This is especially beneficial when passing functions as props to child components, preventing unnecessary re-renders. It is often used with `React.memo()` to ensure child components do not re-render when unnecessary. Below is an exmple of how to use the `useCallback()` hook:

  ```javascript
  import React, { useState, useCallback } from 'react';

  const ParentComponent = () => {
    const [count, setCount] = useState(0);

    // Define a function that increments the count state
    const incrementCount = () => {
      setCount(count + 1);
    };

    // Memoize the incrementCount function using useCallback
    const memoizedIncrement = useCallback(incrementCount, [count]);

    return (
      <div>
        <p>Count: {count}</p>
        <ChildComponent onIncrement={memoizedIncrement} />
      </div>
    );
  };

  const ChildComponent = React.memo(({ onIncrement }) => {
    console.log('Child component rendered');
    return (
      <div>
        <button onClick={onIncrement}>Increment Count</button>
      </div>
    );
  });

  export default ParentComponent;

  ```

  In the code above, the `ParentComponent` is responsible for managing a state variable named `count` and introduces a function called `incrementCount`, which handles the incrementation of the count. Utilizing the `useCallback` hook, the `incrementCount` function is memoized, guaranteeing its stability across renders unless any of its dependencies, in this case, `count`, undergo changes.

  On the other hand, the `ChildComponent` is a component nested within the parent. It receives the memoized `onIncrement` function from the parent as a prop. 

  To optimize performance and prevent unnecessary re-renders when the props remain constant, the `ChildComponent` is wrapped with `React.memo()`. This ensures that the child component will only re-render when its props, specifically the memoized function, experience changes, contributing to a more efficient rendering process.

  ==It's important to note that== `==useCallback==` ==should be used sparingly and only for performance-critical parts of your application. Overusing== `==useCallback==` ==can actually lead to worse performance due to the overhead of memoization itself. Always measure the performance impact before and after using== `==useCallback==` ==to ensure it's having the desired effect.==

  ### Throttling and Debouncing Events

  Throttling in React is a technique used to limit the number of times a function or an event handler is invoked. It ensures that the function is called at a specified interval, preventing it from being executed too frequently. 

  Throttling allows you to control the rate at which the function is called by setting up a minimum time interval between each function invocation. If the function is called multiple times within that interval, only the first invocation is executed, and subsequent invocations are ignored until the interval elapses

  Now, let's illustrate throttling with a code example. First, without t==hrottling:==

  ```javascript
  // Without throttling, this function will be called every time the event is triggered
  function handleResize() {
    console.log('Window resized');
  }

  window.addEventListener('resize', handleResize);

  ```

  ==With throttling, we can limit how often the== `==handleResize==` ==function is called:==

  ```actionscript
  // Throttling function
  function throttle(func, delay) {
    let lastCall =  0;
    return function(...args) {
      const now = new Date().getTime();
      if (now - lastCall < delay) {
        return;
      }
      lastCall = now;
      func(...args);
    };
  }

  // Throttled event handler
  const throttledHandleResize = throttle(handleResize,  200);

  window.addEventListener('resize', throttledHandleResize)

  ```

  In this example, the `throttle` function wraps `handleResize` and ensures it's not called more often than every 200 milliseconds. If the `resize` event fires more frequently than that, the `handleResize` function will only be executed once every 200 milliseconds, reducing the potential for performance issues caused by rapid, repeated function calls

  ==Debouncing, on the other hand, is also used to limit the number of times a function or an event handler is invoked. It ensures that the function is called only after a certain period of inactivity. Debouncing allows you to postpone the function call until the user has finished typing or a specific time has elapsed since the last event.==

  For example, imagine you have a search input field and want to trigger a search API request only when the user has finished typing for a certain duration, like `300ms`. 

  With debouncing, the search function will only be invoked after the user stops typing for` 300ms`. If the user continues typing within that interval, the function call will be delayed until the pause occurs. Without debouncing, the function will be called for every keystroke, potentially leading to excessive function calls and unnecessary computation. let's demonstrate with a code example:

  ```javascript
  import React, { useState, useEffect } from 'react';

  const SearchComponent = () => {
    const [searchTerm, setSearchTerm] = useState('');

    // Function to simulate a search API request
    const searchAPI = (query) => {
      console.log(`Searching for: ${query}`);
      // In a real application, you would make an API request here
    };

    // Debounce function to delay the searchAPI call
    const debounce = (func, delay) => {
      let timeoutId;
      return function (...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
          func(...args);
        }, delay);
      };
    };

    // Debounced search function
    const debouncedSearch = debounce(searchAPI, 300);

    // useEffect to watch for changes in searchTerm and trigger debouncedSearch
    useEffect(() => {
      debouncedSearch(searchTerm);
    }, [searchTerm, debouncedSearch]);

    // Event handler for the search input
    const handleSearchChange = (event) => {
      setSearchTerm(event.target.value);
    };

    return (
      <div>
        <label htmlFor="search">Search:</label>
        <input
          type="text"
          id="search"
          value={searchTerm}
          onChange={handleSearchChange}
          placeholder="Type to search..."
        />
      </div>
    );
  };

  export default SearchComponent;

  ```

  With this setup, the `searchAPI` function will only be invoked after the user stops typing for 300ms, preventing excessive API requests and improving the overall performance of the search functionality.

  ### Code Splitting

  Code splitting in React is a technique used to split a large JavaScript bundle into smaller, manageable chunks. It helps improve performance by loading only the necessary code for a specific part of an application rather than loading the entire bundle upfront. 

  When you develop a new React application, all your JavaScript code is typically bundled together into a single file. This file contains all the components, libraries, and other code required for your application to function. But as your application grows, the bundle size can become quite large, resulting in slow initial load times for your users.

  Code splitting ==allows you to divide a single bundle into multiple chunks, which can be loaded selectively based on the current needs of your application. Instead of downloading the entire bundle upfront,== only the necessary code is fetched and executed when a user visits a particular page or triggers a specific action.

  Below is a basic example of code splitting:

  ```javascript
  // AsyncComponent.js
  import React, { lazy, Suspense } from 'react';

  const DynamicComponent = lazy(() => import('./DynamicComponent'));

  const AsyncComponent = () => (
    <Suspense fallback={<div>Loading...</div>}>
      <DynamicComponent />
    </Suspense>
  );

  export default AsyncComponent;


  // DynamicComponent.js
  import React from 'react';

  const DynamicComponent = () => (
    <div>
      <p>This is a dynamically loaded component!</p>
    </div>
  );

  export default DynamicComponent;

  ```

  ==In this example,== `==AsyncComponent==` ==is a component that uses== `==lazy==` ==and== `==Suspense==` ==to perform code splitting. The== `==DynamicComponent==` ==is dynamically imported using the import() syntax.== 

  When `AsyncComponent` is rendered, React will load `DynamicComponent` only when it is needed, reducing the initial bundle size and improving the application's performance. The fallback prop in Suspense specifies what to render while waiting for the dynamic import to resolve, providing a better user experience during the loading process.

  ### React Fragments

  React Fragments are a feature introduced in [React 16.2](https://legacy.reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html) that allows you to group multiple elements together without adding an additional DOM node. This is particularly useful when you need to return multiple elements from a component's render method, but you don't want to introduce unnecessary DOM elements that could affect the layout or styles of your application.

  Imagine you are arranging books on a bookshelf. Each book represents a React component, and the bookshelf represents the DOM. 

  Normally, if you have multiple books, you might want to group them together under a category label (analogous to a DOM element like a `<div>`). But sometimes you just want to place the books side by side without a label because the label itself doesn't hold any value and only takes up physical space. 

  React Fragments are like the option to arrange the books without a label, saving space and making the arrangement cleaner.

  Here's an example of how to utilize React fragments:

  ```actionscript
  import React from 'react';

  function BookShelf() {
    return (
      <>
        <Book title="React for Beginners" />
        <Book title="Mastering Redux" />
        <Book title="JavaScript Essentials" />
      </>
    );
  }

  function Book({ title }) {
    return <li>{title}</li>;
  }

  export default BookShelf;

  ```

  In this example, the `BookShelf` component returns a list of `Book` components without wrapping them in a `<div>` or other unnecessary DOM element. Instead, it uses the `<>` shorthand syntax for React Fragments. 

  This results in a cleaner DOM structure, which can improve the performance of your React application by reducing the number of elements that the browser has to process and render. Using fragments can also reduce unnecessary markup and contribute to a cleaner and more efficient render tree.

  ### Web Workers

  JavaScript operates as a single-threaded application designed to handle synchronous tasks. 

  When a web page is being rendered, JavaScript executes multiple tasks, including manipulating DOM elements, managing UI interactions, handling API response data, and enabling CSS animations, all within a single thread. Despite its efficiency in managing these tasks, executing them in a single thread can sometimes lead to performance bottlenecks.

  ==Web Workers serve as a solution to alleviate the burden on the main thread. They allow the execution of scripts in the background on a separate thread, distinct from the main JavaScript thread.== 

  This separation enables the handling of computationally intensive tasks, execution of long-running operations, or management of tasks that might otherwise block the main thread. By doing so, Web Workers contribute to maintaining user interface responsiveness and overall application performance.

  To use web worker in React, create a new JavaScript file that will contain the code for the worker thread:

  ```php
  // worker.js
  self.onmessage = function(event) {
    var input = event.data;
    var result = performHeavyComputation(input);
    postMessage(result);
  };

  function performHeavyComputation(input) {
    // Insert your heavy computation logic here
    return input *   2; // Just a placeholder operation
  }

  ```

  In your React component, instantiate the Web Worker and establish a communication channel with it:

  ```javascript
  import React, { useEffect, useRef } from 'react';

  function MyComponent() {
    const workerRef = useRef();

    useEffect(() => {
      // Initialize the worker
      workerRef.current = new Worker('path-to-your-worker-file.js');

      // Handle incoming messages from the worker
      workerRef.current.onmessage = (event) => {
        console.log('Message received from worker:', event.data);
      };

      // Cleanup the worker when the component unmounts
      return () => {
        workerRef.current.terminate();
      };
    }, []);

    // Function to send a message to the worker
    const sendMessageToWorker = (message) => {
      workerRef.current.postMessage(message);
    };

    // Rest of your component
    return (
      // ...
    );
  }


  ```

  In this example, a Web Worker is initialized in the `useEffect` hook and stored in a ref for future use. Messages from the worker are handled with an `onmessage` event listener, and the worker is terminated when the component is unmounted to clean up resources. The `sendMessageToWorker` function demonstrates how to communicate with the worker using `postMessage`

  ### UseTransition Hook

  ==The== `==useTransition==` ==hook in React plays a pivotal role in improving the performance of applications by allowing the marking of state updates as non-blocking transitions. This capability enables React to defer rendering for these updates, preventing UI blocking and enhancing overall responsiveness.== 

  When utilizing `useTransition,` state updates within the `startTransition` function are treated as low-priority transitions, susceptible to interruption by higher-priority state updates. So if a high-priority update occurs during a transition, React may prioritize finishing the high-priority update, interrupting the ongoing transition.

  This non-blocking transition mechanism is valuable in preventing UI blocking during intensive operations such as data fetching or large-scale updates. By deferring the rendering of components associated with transition updates, React ensures that the user interface remains responsive even in scenarios where the UI might otherwise become unresponsive.

  This example demonstrates the use of `useTransition` in a React component:

  ```javascript
  import React, { useState, useTransition } from 'react';

  function MyComponent() {
    const [state, setState] = useState(initialState);
    const [isPending, startTransition] = useTransition();

    function handleClick() {
      startTransition(() => {
        setState(newState); // This state update is marked as a transition
      });
    }

    return (
      <>
        {/* Your component JSX */}
        <button onClick={handleClick}>Update State</button>
        {isPending && <div>Loading...</div>}
      </>
    );
  }

  ```

  This example showcases how React avoids blocking the UI during transitions triggered by user actions, allowing for interruption if higher-priority state updates are detected. 

  Note that `useTransition` is part of the Concurrent Mode API, introduced in React 18 and later versions. As a powerful tool for altering the default behavior of state updates, make sure you use it with care, considering the specific implications of deferring rendering within the context of your application.

  ## Conclusion

  Optimizing the performance of a React application involves a combination of strategies, from the fundamental understanding of React's diffing algorithm to leveraging built-in features and third-party tools. 

  By applying these techniques judiciously, you can create applications that are not only visually appealing but also performant, leading to a better overall user experience.

  ---

  ---

   Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. [Get started](https://www.freecodecamp.org/learn/)

raw_content:
  ---
  id: c4ab379e-cc84-11ee-b3d0-afc69cc1061d
  title: |
    React Optimization Techniques to Help You Write More Performant Code
  status: ARCHIVED
  tags:
    - read-later
    - RSS
  date_added: 2024-02-15 21:57:28
  url_omnivore: |
    https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2
  url_original: |
    https://www.freecodecamp.org/news/react-performance-optimization-techniques/
  ---

  # React Optimization Techniques to Help You Write More Performant Code

  ## Highlights

  List visualization, or windowing, involves rendering only the items currently visible on the screen. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#9fb355ed-811d-44e7-aa61-12c0b18db7e2) 

  ---

  Lazy loading allows you to defer or delay the loading of images until they are needed or visible to the user instead of loading all the images on page load. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#f9c0a23a-9d66-4a80-bb4c-a4f2806ed1aa) 

  ---

  Another approach is to use the [intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection%5FObserver%5FAPI), which is a web API that allows you to detect when an element enters or exists the viewport efficiently. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#a44706c9-5cce-4765-ac78-77efdca40a30) 

  ---

  Memoization in React is a technique used to optimize the performance of functional components by caching the results of expensive computations or function calls. It's particularly useful when dealing with computationally intensive or frequently called functions with the same input values, as it helps avoid redundant calculations and improves the overall efficiency of the application. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#051f1544-65d2-4eae-87c6-bb7f226c4fa7) 

  ---

  Below is an example on how to use the `React.memo` with a functional component:

  ```javascript
  import React from 'react';

  const Post = ({ signedIn, post }) => {
    console.log('Rendering Post');
    return (
      
        {post.title}
        {post.content}
        {signedIn && Edit Post}
      
    );
  };

  export default React.memo(Post);

  ```

  In the code above, `Post` (functional component) depends on the `signedIn` and `post` props. By wrapping it with `React.memo()`, React will only re-render the `Post` component if either `signedIn` or `post` changes. 

  > [!note]
  > You can memoize complete components to prevent re-renders if the props doesn't change, this use `React.memo()`, not the `useMemo()` hook

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#f0735f25-7bdf-4562-84cb-6f93f3a0b5a6) 

  ---

  The `useMemo()` hook optimizes performance by memoizing the result of a function call or an expensive computation. It caches the result and recalculates it only when the input values change. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#360c5717-251f-40d7-9dd8-24bccb26a59b) 

  ---

  The `useCallback()` hook in React is used to memoize a function instead of memoizing the function result. It is particularly useful when passing events as props to child components to prevent unnecessary re-renders. 

  `useCallback()` memoizes the function, ensuring it remains the same across re-renders as long as the dependencies haven't changed. 

  This is especially beneficial when passing functions as props to child components, preventing unnecessary re-renders. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#c01f0766-7401-44ae-9888-ee1da6d10ca7) 

  ---

  It's important to note that `useCallback` should be used sparingly and only for performance-critical parts of your application. Overusing `useCallback` can actually lead to worse performance due to the overhead of memoization itself. Always measure the performance impact before and after using `useCallback` to ensure it's having the desired effect. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#4292a582-b6cb-44e9-baff-3be6a127ed14) 

  ---

  Throttling in React is a technique used to limit the number of times a function or an event handler is invoked. It ensures that the function is called at a specified interval, preventing it from being executed too frequently. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#5e73fc50-7717-449d-a27f-f3bb87fd100f) 

  ---

  Debouncing, on the other hand, is also used to limit the number of times a function or an event handler is invoked. It ensures that the function is called only after a certain period of inactivity. Debouncing allows you to postpone the function call until the user has finished typing or a specific time has elapsed since the last event. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#2926578f-0a36-4acc-8c03-5d740981bb04) 

  ---

  // Debounce function to delay the searchAPI call const debounce = (func, delay) => {let timeoutId;return function (...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() \=> { func(...args); }, delay); }; }; 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#131255d3-029e-43c9-8c5e-0489e786e425) 

  ---

  Code splitting in React is a technique used to split a large JavaScript bundle into smaller, manageable chunks. It helps improve performance by loading only the necessary code for a specific part of an application rather than loading the entire bundle upfront. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#9f66f0bf-2185-499a-a4f1-591e5af60182) 

  ---

  In this example, `AsyncComponent` is a component that uses `lazy` and `Suspense` to perform code splitting. The `DynamicComponent` is dynamically imported using the import() syntax. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#aff44dd9-50f2-4ad2-8f93-d52e6ab0988d) 

  ---

  Web Workers serve as a solution to alleviate the burden on the main thread. They allow the execution of scripts in the background on a separate thread, distinct from the main JavaScript thread. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#16416227-0870-4503-a67d-76418223953a) 

  ---

  The `useTransition` hook in React plays a pivotal role in improving the performance of applications by allowing the marking of state updates as non-blocking transitions. This capability enables React to defer rendering for these updates, preventing UI blocking and enhancing overall responsiveness. 

  [source](https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2#0622edf4-fa98-4848-aeee-174fa1cbde22) 

  ---

  ## Original

  ![React Optimization Techniques to Help You Write More Performant Code](https://proxy-prod.omnivore-image-cache.app/2000x1333,sjdHfCDBz1P_bmEqEy9XMcgTCv4J-TX2BuonAzVYMZmk/https://www.freecodecamp.org/news/content/images/size/w2000/2024/02/pexels-howard-adams-575835--1-.jpg) 

  Performance optimization is a critical aspect of developing web applications. Users expect applications to load quickly and respond to their interactions smoothly. 

  In the React ecosystem, performance optimization techniques can significantly enhance the user experience by reducing load times and improving responsiveness.

  In this article, we will discuss eight effective techniques for optimizing the performance of your React application.

  ## Table of Contents

  1. [Why Performance Optimization is Important](#why-performance-optimization-is-important)
  2. [List visualization](#list-visualization)
  3. [Lazy Loading Images](#lazy-loading-images)
  4. [Memoization](#memoization)
  5. [Throttling and Debouncing Events](#throttling-and-debouncing-events)
  6. [Code Splitting](#code-splitting)
  7. [React Fragments](#react-fragments)
  8. [Web Workers](#web-workers)
  9. [UseTransition Hook](#usetransition-hook)
  10. [Conclusion](#conclusion)

  ## Why Performance Optimization is Important

  Optimizing the performance of your React application is crucial for several reasons:

  * **Better User Experience:** A slow-loading or laggy application can lead to a poor user experience, negatively impacting your business. Users expect fast and responsive interactions, and performance optimization helps deliver that.
  * **Improved SEO:** Search engines like Google consider page load times and overall performance when ranking websites. A well-optimized application will rank higher in search results, making it more visible to potential users.
  * **Reduced Bounce Rates:** If your application takes too long to load or respond, users will likely leave and never return. By optimizing performance, you can reduce bounce rates and increase engagement.
  * **Cost Savings** A performant application requires fewer resources (like servers and memory) to handle the same workload. This means lower hosting costs and reduced infrastructure needs.
  * **Competitive Advantage:** A fast and efficient application sets you apart from competitors whose applications may be slower or less optimized. According to research by [Portent](https://www.portent.com/blog/analytics/research-site-speed-hurting-everyones-revenue.htm), a website that loads within one second has a conversion rate five times higher than a site that takes ten seconds to load. Therefore, ensuring your React applications perform well is crucial for retaining users and maintaining a competitive edge.

  ## 8 React Performance Optimization Techniques

  Below are eight React performance optimization techniques you can use to speed up your applications.

  ### List visualization

  ==List visualization, or windowing, involves rendering only the items currently visible on the screen.== 

  When dealing with a large number of items in a list, rendering all the items at once can lead to slow performance and consume a significant amount of memory. List virtualization tackles this issue by rendering only a subset of the list items currently visible within the view, which conserves resources as the users scroll through the list.

  The virtualization technique dynamically replaces rendered items with new ones, keeping the visible portion of the list updated and responsive. It efficiently allows you to render large lists or tabular data by only rendering the visible portion, recycling components as needed, and optimizing scroll performance.

  There are different approaches to implementing list visualization in React, and one is using a popular library called [React Virtualized](https://www.npmjs.com/package/react-virtualized). 

  To install `react-virtualized`, you can use the following command:

  ```sql
  npm install react-virtualized --save
  ```

  After installing `react-virtualized`, you can import the required components and styles. Below is an example of how to use the `List` component to create a virtualized list:

  ```javascript
  import React from 'react';
  import { List } from 'react-virtualized';
  import 'react-virtualized/styles.css'; // Import styles

  // Your list data
  const list = Array(5000).fill().map((_, index) => ({
    id: index,
    name: `Item ${index}`
  }));

  // Function to render each row
  function rowRenderer({ index, key, style }) {
    return (
      <div key={key} style={style}>
        {list[index].name}
      </div>
    );
  }

  // Main component
  function MyVirtualizedList() {
    return (
      <List
        width={300}
        height={300}
        rowCount={list.length}
        rowHeight={20}
        rowRenderer={rowRenderer}
      />
    );
  }

  export default MyVirtualizedList;

  ```

  In this example, `List` is the main component provided by `react-virtualized`. The `rowRenderer` function defines how each row should be rendered. The `width`, `height`, `rowCount`, `rowHeight`, and `rowRenderer` props are essential for configuring the list's behavior and appearance. 

  React applications can handle massive amounts of data by leveraging list virtualization without sacrificing performance or user experience.

  ### Lazy Loading Images

  Similar to the list virtualization technique, lazy loading images prevents the creation of unnecessary DOM nodes, thereby boosting performance. Lazy loading allows you to defer or delay the loading of images until they are needed or visible to the user instead of loading all the images on page load.

  The concept behind lazy loading is to initiate the load of a placeholder or a small low-resolution version of the image, typically a small-sized thumbnail or a blurred placeholder. As the user scrolls or interacts with the page, the actual image is loaded dynamically, replacing the placeholder when the user enters the viewport or when it becomes visible.

  Lazy loading in React can be achieved using various libraries and techniques. One of the popular libraries is the [react-lazyload](https://www.npmjs.com/package/react-lazyload). 

  To install `react-lazyload`, you can use the following command:

  ```sql
  npm install --save react-lazyload
  ```

  Below is an example of a simple React component that uses `react-lazyload` to implement lazy loading for images:

  ```javascript
  import React from 'react';
  import LazyLoad from 'react-lazyload';

  const MyLazyLoadedImage = ({ src, alt }) => {
    return (
      <LazyLoad height={200} offset={100}>
        {/* The height and offset props control when the image should start loading */}
        <img src={src} alt={alt} />
      </LazyLoad>
    );
  };

  export default MyLazyLoadedImage;

  ```

  In this example, `MyLazyLoadedImage` uses the `LazyLoad` component from `react-lazyload`. The `height` prop specifies the height of the placeholder, and the `offset` prop determines how far below the viewport the placeholder should start loading.

  ==Another approach is to use the== ==[intersection observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection%5FObserver%5FAPI)====, which is a web API that allows you to detect when an element enters or exists the viewport efficiently.== Here's how we can use the Intersection Observer API along with the `useEffect` hook in React:

  ```javascript
  import React, { useEffect, useRef } from 'react';

  const IntersectionLazyLoad = ({ src, alt }) => {
    const imageRef = useRef();

    useEffect(() => {
      const options = {
        root: null, // Use the viewport as the root
        rootMargin: '0px', // No margin around the root
        threshold: 0.5, // 50% of the image should be visible
      };

      const observer = new IntersectionObserver(handleIntersection, options);

      if (imageRef.current) {
        observer.observe(imageRef.current);
      }

      return () => {
        // Cleanup the observer when the component is unmounted
        observer.disconnect();
      };
    }, []);

    const handleIntersection = (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          // Load the image when it becomes visible
          imageRef.current.src = src;
          imageRef.current.alt = alt;
        }
      });
    };

    return <img ref={imageRef} style={{ height: '200px' }} alt="Placeholder" />;
  };

  export default IntersectionLazyLoad;

  ```

  In this example, `IntersectionLazyLoad` uses the Intersection Observer API to determine when the image becomes visible in the viewport. 

  By utilizing this API along with React `useEffect` hook, you can implement your custom lazy loading solution for images in React.

  ### Memoization

  Memoization in React is a technique used to optimize the performance of functional components by caching the results of expensive computations or function calls. It's particularly useful when dealing with computationally intensive or frequently called functions with the same input values, as it helps avoid redundant calculations and improves the overall efficiency of the application.

  In React, there are three techniques for memoization: `React.memo()`, `useMemo(),` and `useCallback().` Let's delve into the details for each:

  #### How to use `React.memo()`

  This higher-order component wraps purely functional components to prevent re-rendering if the received props remain unchanged.

  By using `React.memo()`, the rendering result is cached based on props. If the props haven't changed since the last render, React reuses the previously rendered result instead of redoing the rendering process. This saves time and resources.

  ==Below is an example on how to use the== `==React====.====memo==` ==with a functional component:==

  ```javascript
  import React from 'react';

  const Post = ({ signedIn, post }) => {
    console.log('Rendering Post');
    return (
      <div>
        <h2>{post.title}</h2>
        <p>{post.content}</p>
        {signedIn && <button>Edit Post</button>}
      </div>
    );
  };

  export default React.memo(Post);

  ```

  ==In the code above,== `==Post==` ==(functional component) depends on the== `==signedIn==` ==and== `==post==` ==props. By wrapping it with== `==React====.====memo====()==`==, React will only re-render the== `==Post==` ==component if either== `==signedIn==` ==or== `==post==` ==changes.==

  You can now use the memoized component like any other component in your application:

  ```javascript
  import React, { useState } from 'react';
  import Post from './Post';

  const App = () => {
    const [signedIn, setSignedIn] = useState(false);
    const post = { title: 'Hello World', content: 'Welcome to my blog!' };

    return (
      <div>
        <Post signedIn={signedIn} post={post} />
        <button onClick={() => setSignedIn(!signedIn)}>
          Toggle Signed In
        </button>
      </div>
    );
  };

  export default App;

  ```

  When you click the `Toggle Signed In` button, it will toggle the `signedIn` state. Since `Post` is wrapped with `React.memo()`, it will only re-render when the `signedIn` prop changes, thus saving rendering time and resources

  #### How to use `useMemo()`

  The `useMemo()` hook optimizes performance by memoizing the result of a function call or an expensive computation. It caches the result and recalculates it only when the input values change. Below is an example on how to use the `useMemo` hook in functional component:

  ```javascript
  import React, { useMemo } from 'react';

  function App() {
    const [count, setCount] = React.useState(0);
    const [otherState, setOtherState] = React.useState('');

    const expensiveComputation = (num) => {
      let i =  0;
      while (i <  1000000000) i++;
      return num * num;
    };

    const memoizedValue = useMemo(() => expensiveComputation(count), [count]);

    return (
      <div>
        <p>Count: {count}</p>
        <p>Square: {memoizedValue}</p>
        <button onClick={() => setCount(count +  1)}>Increase Count</button>
        <input type="text" onChange={(e) => setOtherState(e.target.value)} />
      </div>
    );
  }

  export default App;

  ```

  In the code above, the `expensiveComputation` function simulates a resource-intensive operation, like squaring a number. 

  The `useMemo` hook is utilized to cache the result of this computation. The memoized value, stored in `memoizedValue`, is only recalculated when the `count` state changes, as `count` is specified as a dependency in the `useMemo` dependency array. Consequently, clicking the `Increase Count` button increments the `count` state, triggering a recalculation of the memoized value. 

  Conversely, changing the `otherState` via the input field does not prompt a recalculation, as `otherState` is not included in the `useMemo` dependency array.

  #### How to use `useCallback()`

  The `useCallback()` hook in React is used to memoize a function instead of memoizing the function result. It is particularly useful when passing events as props to child components to prevent unnecessary re-renders. 

  `useCallback()` memoizes the function, ensuring it remains the same across re-renders as long as the dependencies haven't changed. 

  This is especially beneficial when passing functions as props to child components, preventing unnecessary re-renders. It is often used with `React.memo()` to ensure child components do not re-render when unnecessary. Below is an exmple of how to use the `useCallback()` hook:

  ```javascript
  import React, { useState, useCallback } from 'react';

  const ParentComponent = () => {
    const [count, setCount] = useState(0);

    // Define a function that increments the count state
    const incrementCount = () => {
      setCount(count + 1);
    };

    // Memoize the incrementCount function using useCallback
    const memoizedIncrement = useCallback(incrementCount, [count]);

    return (
      <div>
        <p>Count: {count}</p>
        <ChildComponent onIncrement={memoizedIncrement} />
      </div>
    );
  };

  const ChildComponent = React.memo(({ onIncrement }) => {
    console.log('Child component rendered');
    return (
      <div>
        <button onClick={onIncrement}>Increment Count</button>
      </div>
    );
  });

  export default ParentComponent;

  ```

  In the code above, the `ParentComponent` is responsible for managing a state variable named `count` and introduces a function called `incrementCount`, which handles the incrementation of the count. Utilizing the `useCallback` hook, the `incrementCount` function is memoized, guaranteeing its stability across renders unless any of its dependencies, in this case, `count`, undergo changes.

  On the other hand, the `ChildComponent` is a component nested within the parent. It receives the memoized `onIncrement` function from the parent as a prop. 

  To optimize performance and prevent unnecessary re-renders when the props remain constant, the `ChildComponent` is wrapped with `React.memo()`. This ensures that the child component will only re-render when its props, specifically the memoized function, experience changes, contributing to a more efficient rendering process.

  ==It's important to note that== `==useCallback==` ==should be used sparingly and only for performance-critical parts of your application. Overusing== `==useCallback==` ==can actually lead to worse performance due to the overhead of memoization itself. Always measure the performance impact before and after using== `==useCallback==` ==to ensure it's having the desired effect.==

  ### Throttling and Debouncing Events

  Throttling in React is a technique used to limit the number of times a function or an event handler is invoked. It ensures that the function is called at a specified interval, preventing it from being executed too frequently. 

  Throttling allows you to control the rate at which the function is called by setting up a minimum time interval between each function invocation. If the function is called multiple times within that interval, only the first invocation is executed, and subsequent invocations are ignored until the interval elapses

  Now, let's illustrate throttling with a code example. First, without t==hrottling:==

  ```javascript
  // Without throttling, this function will be called every time the event is triggered
  function handleResize() {
    console.log('Window resized');
  }

  window.addEventListener('resize', handleResize);

  ```

  ==With throttling, we can limit how often the== `==handleResize==` ==function is called:==

  ```actionscript
  // Throttling function
  function throttle(func, delay) {
    let lastCall =  0;
    return function(...args) {
      const now = new Date().getTime();
      if (now - lastCall < delay) {
        return;
      }
      lastCall = now;
      func(...args);
    };
  }

  // Throttled event handler
  const throttledHandleResize = throttle(handleResize,  200);

  window.addEventListener('resize', throttledHandleResize)

  ```

  In this example, the `throttle` function wraps `handleResize` and ensures it's not called more often than every 200 milliseconds. If the `resize` event fires more frequently than that, the `handleResize` function will only be executed once every 200 milliseconds, reducing the potential for performance issues caused by rapid, repeated function calls

  ==Debouncing, on the other hand, is also used to limit the number of times a function or an event handler is invoked. It ensures that the function is called only after a certain period of inactivity. Debouncing allows you to postpone the function call until the user has finished typing or a specific time has elapsed since the last event.==

  For example, imagine you have a search input field and want to trigger a search API request only when the user has finished typing for a certain duration, like `300ms`. 

  With debouncing, the search function will only be invoked after the user stops typing for` 300ms`. If the user continues typing within that interval, the function call will be delayed until the pause occurs. Without debouncing, the function will be called for every keystroke, potentially leading to excessive function calls and unnecessary computation. let's demonstrate with a code example:

  ```javascript
  import React, { useState, useEffect } from 'react';

  const SearchComponent = () => {
    const [searchTerm, setSearchTerm] = useState('');

    // Function to simulate a search API request
    const searchAPI = (query) => {
      console.log(`Searching for: ${query}`);
      // In a real application, you would make an API request here
    };

    // Debounce function to delay the searchAPI call
    const debounce = (func, delay) => {
      let timeoutId;
      return function (...args) {
        clearTimeout(timeoutId);
        timeoutId = setTimeout(() => {
          func(...args);
        }, delay);
      };
    };

    // Debounced search function
    const debouncedSearch = debounce(searchAPI, 300);

    // useEffect to watch for changes in searchTerm and trigger debouncedSearch
    useEffect(() => {
      debouncedSearch(searchTerm);
    }, [searchTerm, debouncedSearch]);

    // Event handler for the search input
    const handleSearchChange = (event) => {
      setSearchTerm(event.target.value);
    };

    return (
      <div>
        <label htmlFor="search">Search:</label>
        <input
          type="text"
          id="search"
          value={searchTerm}
          onChange={handleSearchChange}
          placeholder="Type to search..."
        />
      </div>
    );
  };

  export default SearchComponent;

  ```

  With this setup, the `searchAPI` function will only be invoked after the user stops typing for 300ms, preventing excessive API requests and improving the overall performance of the search functionality.

  ### Code Splitting

  Code splitting in React is a technique used to split a large JavaScript bundle into smaller, manageable chunks. It helps improve performance by loading only the necessary code for a specific part of an application rather than loading the entire bundle upfront. 

  When you develop a new React application, all your JavaScript code is typically bundled together into a single file. This file contains all the components, libraries, and other code required for your application to function. But as your application grows, the bundle size can become quite large, resulting in slow initial load times for your users.

  Code splitting ==allows you to divide a single bundle into multiple chunks, which can be loaded selectively based on the current needs of your application. Instead of downloading the entire bundle upfront,== only the necessary code is fetched and executed when a user visits a particular page or triggers a specific action.

  Below is a basic example of code splitting:

  ```javascript
  // AsyncComponent.js
  import React, { lazy, Suspense } from 'react';

  const DynamicComponent = lazy(() => import('./DynamicComponent'));

  const AsyncComponent = () => (
    <Suspense fallback={<div>Loading...</div>}>
      <DynamicComponent />
    </Suspense>
  );

  export default AsyncComponent;


  // DynamicComponent.js
  import React from 'react';

  const DynamicComponent = () => (
    <div>
      <p>This is a dynamically loaded component!</p>
    </div>
  );

  export default DynamicComponent;

  ```

  ==In this example,== `==AsyncComponent==` ==is a component that uses== `==lazy==` ==and== `==Suspense==` ==to perform code splitting. The== `==DynamicComponent==` ==is dynamically imported using the import() syntax.== 

  When `AsyncComponent` is rendered, React will load `DynamicComponent` only when it is needed, reducing the initial bundle size and improving the application's performance. The fallback prop in Suspense specifies what to render while waiting for the dynamic import to resolve, providing a better user experience during the loading process.

  ### React Fragments

  React Fragments are a feature introduced in [React 16.2](https://legacy.reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html) that allows you to group multiple elements together without adding an additional DOM node. This is particularly useful when you need to return multiple elements from a component's render method, but you don't want to introduce unnecessary DOM elements that could affect the layout or styles of your application.

  Imagine you are arranging books on a bookshelf. Each book represents a React component, and the bookshelf represents the DOM. 

  Normally, if you have multiple books, you might want to group them together under a category label (analogous to a DOM element like a `<div>`). But sometimes you just want to place the books side by side without a label because the label itself doesn't hold any value and only takes up physical space. 

  React Fragments are like the option to arrange the books without a label, saving space and making the arrangement cleaner.

  Here's an example of how to utilize React fragments:

  ```actionscript
  import React from 'react';

  function BookShelf() {
    return (
      <>
        <Book title="React for Beginners" />
        <Book title="Mastering Redux" />
        <Book title="JavaScript Essentials" />
      </>
    );
  }

  function Book({ title }) {
    return <li>{title}</li>;
  }

  export default BookShelf;

  ```

  In this example, the `BookShelf` component returns a list of `Book` components without wrapping them in a `<div>` or other unnecessary DOM element. Instead, it uses the `<>` shorthand syntax for React Fragments. 

  This results in a cleaner DOM structure, which can improve the performance of your React application by reducing the number of elements that the browser has to process and render. Using fragments can also reduce unnecessary markup and contribute to a cleaner and more efficient render tree.

  ### Web Workers

  JavaScript operates as a single-threaded application designed to handle synchronous tasks. 

  When a web page is being rendered, JavaScript executes multiple tasks, including manipulating DOM elements, managing UI interactions, handling API response data, and enabling CSS animations, all within a single thread. Despite its efficiency in managing these tasks, executing them in a single thread can sometimes lead to performance bottlenecks.

  ==Web Workers serve as a solution to alleviate the burden on the main thread. They allow the execution of scripts in the background on a separate thread, distinct from the main JavaScript thread.== 

  This separation enables the handling of computationally intensive tasks, execution of long-running operations, or management of tasks that might otherwise block the main thread. By doing so, Web Workers contribute to maintaining user interface responsiveness and overall application performance.

  To use web worker in React, create a new JavaScript file that will contain the code for the worker thread:

  ```php
  // worker.js
  self.onmessage = function(event) {
    var input = event.data;
    var result = performHeavyComputation(input);
    postMessage(result);
  };

  function performHeavyComputation(input) {
    // Insert your heavy computation logic here
    return input *   2; // Just a placeholder operation
  }

  ```

  In your React component, instantiate the Web Worker and establish a communication channel with it:

  ```javascript
  import React, { useEffect, useRef } from 'react';

  function MyComponent() {
    const workerRef = useRef();

    useEffect(() => {
      // Initialize the worker
      workerRef.current = new Worker('path-to-your-worker-file.js');

      // Handle incoming messages from the worker
      workerRef.current.onmessage = (event) => {
        console.log('Message received from worker:', event.data);
      };

      // Cleanup the worker when the component unmounts
      return () => {
        workerRef.current.terminate();
      };
    }, []);

    // Function to send a message to the worker
    const sendMessageToWorker = (message) => {
      workerRef.current.postMessage(message);
    };

    // Rest of your component
    return (
      // ...
    );
  }


  ```

  In this example, a Web Worker is initialized in the `useEffect` hook and stored in a ref for future use. Messages from the worker are handled with an `onmessage` event listener, and the worker is terminated when the component is unmounted to clean up resources. The `sendMessageToWorker` function demonstrates how to communicate with the worker using `postMessage`

  ### UseTransition Hook

  ==The== `==useTransition==` ==hook in React plays a pivotal role in improving the performance of applications by allowing the marking of state updates as non-blocking transitions. This capability enables React to defer rendering for these updates, preventing UI blocking and enhancing overall responsiveness.== 

  When utilizing `useTransition,` state updates within the `startTransition` function are treated as low-priority transitions, susceptible to interruption by higher-priority state updates. So if a high-priority update occurs during a transition, React may prioritize finishing the high-priority update, interrupting the ongoing transition.

  This non-blocking transition mechanism is valuable in preventing UI blocking during intensive operations such as data fetching or large-scale updates. By deferring the rendering of components associated with transition updates, React ensures that the user interface remains responsive even in scenarios where the UI might otherwise become unresponsive.

  This example demonstrates the use of `useTransition` in a React component:

  ```javascript
  import React, { useState, useTransition } from 'react';

  function MyComponent() {
    const [state, setState] = useState(initialState);
    const [isPending, startTransition] = useTransition();

    function handleClick() {
      startTransition(() => {
        setState(newState); // This state update is marked as a transition
      });
    }

    return (
      <>
        {/* Your component JSX */}
        <button onClick={handleClick}>Update State</button>
        {isPending && <div>Loading...</div>}
      </>
    );
  }

  ```

  This example showcases how React avoids blocking the UI during transitions triggered by user actions, allowing for interruption if higher-priority state updates are detected. 

  Note that `useTransition` is part of the Concurrent Mode API, introduced in React 18 and later versions. As a powerful tool for altering the default behavior of state updates, make sure you use it with care, considering the specific implications of deferring rendering within the context of your application.

  ## Conclusion

  Optimizing the performance of a React application involves a combination of strategies, from the fundamental understanding of React's diffing algorithm to leveraging built-in features and third-party tools. 

  By applying these techniques judiciously, you can create applications that are not only visually appealing but also performant, leading to a better overall user experience.

  ---

  ---

   Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. [Get started](https://www.freecodecamp.org/learn/) 

metadata:
  {"date_added":"2024-02-15 21:57:28","id":"c4ab379e-cc84-11ee-b3d0-afc69cc1061d","status":"ARCHIVED","tags":["read-later","RSS"],"title":"React Optimization Techniques to Help You Write More Performant Code\n","url_omnivore":"https://omnivore.app/me/react-optimization-techniques-to-help-you-write-more-performant--18db03268c2\n","url_original":"https://www.freecodecamp.org/news/react-performance-optimization-techniques/\n"}


path: Omnivore/2024-02-18 - Git Tips 1- Oldies but Goodies.md
sortable_path: Omnivore2024-02-18 - Git Tips 1- Oldies but Goodies.md
title: Git Tips 1: Oldies but Goodies
lead: # Git Tips 1: Oldies but Goodies
checksum: 1b30d34620d4e844e955c4c16356a7b4c860f40895e8d76cf0f36135b9f14449
created: 2024-09-15 00:40:58.00796507+00:00
modified: 2024-09-22 22:40:13.395511052+00:00
body:
  # Git Tips 1: Oldies but Goodies

  ## Highlights

  But there is also `--system` (which probably none of you have used) which writes it to a system-wide config file and `--local` (the default) that writes it to `.git/config` in whatever project you're currently in. 

  [source](https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae#4994949d-07aa-4baf-8b39-7b003dcc4487) 

  ---

  However, there is a _secret fourth_ place that Git can look. If you add to your global config something that looks like this:

  ```cs
  [includeIf "gitdir:~/projects/oss"]
      path = ~/.gitconfig-oss
  ```

  Then Git will look in the `~/.gitconfig-oss` files for values _only if_ the project you are currently working in matches `~/projects/oss` . So, you could have a "work" directory and have work-specific values there (company email address, gpg signing key, etc) and an "oss" directory with values for your open source projects, etc. 

  [source](https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae#400065ab-ea9d-4b03-80c8-aec98b643a27) 

  ---

  One thing that is really not great about using blame in GUI tools is that the CLI has much more powerful tooling for finding something closer to the real story behind your code. 

  [source](https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae#60d94f0c-f71c-4e30-83d7-ed9d4ac07265) 

  ---

  Finally, if you're rebasing or cherry-picking a lot and you've ever run into the same conflict more than once, you can turn on a feature where Git memorizes the conflict and the resolution to it. If it ever sees that same conflict again, it will _automatically_ re-solve it for you.

  You can easily turn it on with the config setting `rerere.enabled` and you can further ask it to automatically stage it for you with `rerere.autoUpdate`

  ```routeros
  $ git config --global rerere.enabled true
  $ git config --global rerere.autoUpdate true
  ``` 

  [source](https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae#7e50290a-231d-4e52-9518-2e8ad22503cf) 

  ---

  ## Original

  ![Git Tips 1: Oldies but Goodies](https://proxy-prod.omnivore-image-cache.app/0x0,sxYzn8TfPTv6_Cwa-Y2CFodsIFgz-ve4_5ZVswMWgFaU/https://blog.gitbutler.com/content/images/size/w500/2024/02/CleanShot-2024-02-08-at-15.54.15@2x.png) 

   Do you know some of the cool stuff in Git that's been around for a while? All the magical -L and -C options in the Git world? Let's find out!

  For the first in our [short series of Git tips](https://blog.gitbutler.com/git-tips-and-tricks/), I wanted to start with stuff that's been around for a while, but it still seems like a lot of people don't know about or don't know how to use. 

  None of this is new, but I find them useful and they're arguably a little obscure. I'm just going to cover:

  * [Conditional Configs](#conditional-configs)
  * [Git Blame and Log with Line Ranges](#git-blame-and-log-with-line-ranges)
  * [Git Blame with Following](https://blog.gitbutler.com/git-tips-1-theres-a-git-config-for-that/git-blame-with-following)
  * [Word Diff](#word-diff)
  * [Resolution Reuse](#reuse-recorded-resolution)

  Let's dig in!

  ## Conditional Configs

  Many of you probably know this, but Git has a cool little key/value store called `git config` which will check in three places for values to use when it's running various commands.

  Every Git user will have [probably been told](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup?ref=blog.gitbutler.com) to run something like this when they first set up:

  ```routeros
  $ git config --global user.name "John Doe"
  $ git config --global user.email johndoe@example.com
  ```

  That adds the `user.name` value to your `~.gitconfig` file. ==But there is also== `==--system==` ==(which probably none of you have used) which writes it to a system-wide config file and== `==--local==` ==(the default) that writes it to== `==.git/====config==` ==in whatever project you're currently in.==

  When Git looks for a value, it will look in that order - local, global, system - for a definition.

  ==However, there is a== _==secret fourth==_ ==place that Git can look. If you add to your global config something that looks like this:==

  ```cs
  [includeIf "gitdir:~/projects/oss"]
      path = ~/.gitconfig-oss
  ```

  ==Then Git will look in the== `==~/.gitconfig-oss==` ==files for values== _==only if==_ ==the project you are currently working in matches== `==~====/projects/====oss==` ==. So, you could have a "work" directory and have work-specific values there (company email address, gpg signing key, etc) and an "oss" directory with values for your open source projects, etc.==

  But `gitdir` is not the only filter you can use. You can also put _branch name_ specific values as a include filter with `onbranch` or you can only include config files if the project you are currently in has a remote matching a specific URL with `hasconfig:remote.*.url` . So like if you have GitHub org specific keys or something.

  Check out [the docs](https://git-scm.com/docs/git-config?ref=blog.gitbutler.com#%5Fincludes) for more.

  ## Git Blame and Log with Line Ranges

  There are a couple of interesting options that you can use with `git blame` that most people don't know about and nearly none of the existing GUIs implement.

  One is the line range option, `-L`. A lot of times, if you're running blame on the command line, you just page the whole file and find the part you're looking for. However, if you want to just display a subsection of your file, you can give it a line range, like `git blame -L 28,43 path/to/file`

  ![](https://proxy-prod.omnivore-image-cache.app/2000x521,s-gI7WBL754ILzJIrAX--1643Ur5JnSBRuJhH1OIoSVk/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-06-at-15.17.13@2x.png)

  git blame -L

  You can also use a semi-strange `:` syntax to give Git a pattern to find the beginning of a block and only blame that block. So in this same situation, I can get the same result by running `git blame -L :'class LocalFile' gitbutler-ui/src/lib/vbranches/types.ts`

  Typically you can use a function name or something for that string.

  The _other_ thing you can do to see similar information in a different way, is to run `git log` with similar options. This will give you all the commits filtered to those that last touched this region of the file. So for example, `git log -L28,43:gitbutler-ui/src/lib/vbranches/types.ts` will give you something like this:

  ![](https://proxy-prod.omnivore-image-cache.app/1330x1554,s1UJYnXDDdzZCHCtQklZ7-TjA6hDYVdPC5VbLP8liK9s/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-06-at-16.42.55@2x.png)

  So instead of being ordered by lines, it sort of gathers all the commits that are shown in the blame and then shows you those commits with code that modified that block in each commit. Basically the same data, but in a different format, more like a story of how that code was put together.

  ## Git Blame with Following

  ==One thing that is really not great about using blame in GUI tools is that the CLI has much more powerful tooling for finding something closer to the real story behind your code.==

  There are many scenarios where this is really valuable. The first is ignoring whitespace changes. Some of the GUIs do that, but not all of them. So if you go and implement a `prettierrc` file, BLAM, now you're the owner of tons of lines of code. The `git blame -w` option will ignore these types of whitespace changes.

  The other great option is `-C` which will look for code movement between files in a commit. So if you refactor a function from one file to another, the normal `git blame` will simply show you as the author in the new file, but the `-C` option will follow that movement and show the last person to actually change those lines of code. Either of these scenarios could be what you're looking for, but I would argue that more often it's the latter.

  If you want Git to try even harder (look for movement in multiple commits or in _all_ of your commits) you can pass `-C` up to three times.

   Also, your GUI _does not_ do this (most likely, I can't speak for all of them).

  So, let's look at the VS Code GitLens blame output of the previous example:

  ![](https://proxy-prod.omnivore-image-cache.app/2000x592,sJ273c2NDlCitPAhifHX1YOXueBGDaERpAceyt8mJCaY/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-06-at-15.17.36@2x.png)

  git blame in GitLens VS Code plugin

  Ok, looks good. Kiril wrote most of this code it appears. Let's now look at the same block with `git blame -w -C -C -C`

  ![](https://proxy-prod.omnivore-image-cache.app/2000x502,syC33t00Kd2IdxqHirm_tueemYzBlfSNjnzjwqhanPd4/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-06-at-15.17.01@2x.png)

  git blame -C -C -C

  Now we can see that Git has followed this hunk of code from file to file over the course of multiple renames. 

  Also, Kiril only really owns a few lines of code, Mattias actually wrote big hunks of it. If we want to know about those lines, it's much better to ask Mattias rather than Kiril, as our GUI blame would suggest. 

  ## Word Diff

  This is incredibly minor, and some GUIs have nice versions of this (I find GitHub's better than what I'm about to show you, since it subtly does both) but if you _ARE_ running `git diff` on the command line and there is a line change where something small changed within it, you can change Git's default format to word-based rather than line based with the `--word-diff` option.

  ![](https://proxy-prod.omnivore-image-cache.app/1486x404,sqppS5EQzYWWKi-Y-3i2K_-fL090EekLjppG43Mst2qA/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-08.19.47@2x.png)

  normal, line-based git diff

  ![](https://proxy-prod.omnivore-image-cache.app/1428x628,sNHm1bxMfK2e5r2nox_blv7vaKHj-6Lf3FOTYgeOkya8/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-08.19.28@2x.png)

  super cool git diff --word-diff

  ## Reuse Recorded Resolution

  ==Finally, if you're rebasing or cherry-picking a lot and you've ever run into the same conflict more than once, you can turn on a feature where Git memorizes the conflict and the resolution to it. If it ever sees that same conflict again, it will== _==automatically==_ ==re-solve it for you.==

  ==You can easily turn it on with the config setting== `==rerere====.enabled==` ==and you can further ask it to automatically stage it for you with== `==rerere.====auto====Update==`

  ```routeros
  $ git config --global rerere.enabled true
  $ git config --global rerere.autoUpdate true
  ```

  ![](https://proxy-prod.omnivore-image-cache.app/1318x294,sX3LU-meJf_hU5ed2UJyHDxUFbVHUmP8ABem5jqgxwn4/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-08.21.07@2x.png)

  a conflict... always remembers

  Then, the next time you get a merge conflict that it's seen before, magic!

  ![](https://proxy-prod.omnivore-image-cache.app/1318x286,sW3jEfpz3Hik-ekQ2xOm_Vujx2Vk8MKKYqniW9WP3wo8/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-08.23.20@2x.png)

  automatically fix it the next time

  ## Up Next

  Again, all of this has been in Git for a while, but if you don't know... now you know. 

  Next up is [New Stuff in Git](https://blog.gitbutler.com/git-tips-2-new-stuff-in-git/).

  ### Subscribe to new posts.

raw_content:
  ---
  id: a6c66f79-fd38-4d76-b05a-9c5d7dc9119f
  title: |
    Git Tips 1: Oldies but Goodies
  status: ARCHIVED
  tags:
    - read-later
    - dotfiles
    - git
  date_added: 2024-02-18 11:02:02
  url_omnivore: |
    https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae
  url_original: |
    https://blog.gitbutler.com/git-tips-1-theres-a-git-config-for-that/
  ---

  # Git Tips 1: Oldies but Goodies

  ## Highlights

  But there is also `--system` (which probably none of you have used) which writes it to a system-wide config file and `--local` (the default) that writes it to `.git/config` in whatever project you're currently in. 

  [source](https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae#4994949d-07aa-4baf-8b39-7b003dcc4487) 

  ---

  However, there is a _secret fourth_ place that Git can look. If you add to your global config something that looks like this:

  ```cs
  [includeIf "gitdir:~/projects/oss"]
      path = ~/.gitconfig-oss
  ```

  Then Git will look in the `~/.gitconfig-oss` files for values _only if_ the project you are currently working in matches `~/projects/oss` . So, you could have a "work" directory and have work-specific values there (company email address, gpg signing key, etc) and an "oss" directory with values for your open source projects, etc. 

  [source](https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae#400065ab-ea9d-4b03-80c8-aec98b643a27) 

  ---

  One thing that is really not great about using blame in GUI tools is that the CLI has much more powerful tooling for finding something closer to the real story behind your code. 

  [source](https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae#60d94f0c-f71c-4e30-83d7-ed9d4ac07265) 

  ---

  Finally, if you're rebasing or cherry-picking a lot and you've ever run into the same conflict more than once, you can turn on a feature where Git memorizes the conflict and the resolution to it. If it ever sees that same conflict again, it will _automatically_ re-solve it for you.

  You can easily turn it on with the config setting `rerere.enabled` and you can further ask it to automatically stage it for you with `rerere.autoUpdate`

  ```routeros
  $ git config --global rerere.enabled true
  $ git config --global rerere.autoUpdate true
  ``` 

  [source](https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae#7e50290a-231d-4e52-9518-2e8ad22503cf) 

  ---

  ## Original

  ![Git Tips 1: Oldies but Goodies](https://proxy-prod.omnivore-image-cache.app/0x0,sxYzn8TfPTv6_Cwa-Y2CFodsIFgz-ve4_5ZVswMWgFaU/https://blog.gitbutler.com/content/images/size/w500/2024/02/CleanShot-2024-02-08-at-15.54.15@2x.png) 

   Do you know some of the cool stuff in Git that's been around for a while? All the magical -L and -C options in the Git world? Let's find out!

  For the first in our [short series of Git tips](https://blog.gitbutler.com/git-tips-and-tricks/), I wanted to start with stuff that's been around for a while, but it still seems like a lot of people don't know about or don't know how to use. 

  None of this is new, but I find them useful and they're arguably a little obscure. I'm just going to cover:

  * [Conditional Configs](#conditional-configs)
  * [Git Blame and Log with Line Ranges](#git-blame-and-log-with-line-ranges)
  * [Git Blame with Following](https://blog.gitbutler.com/git-tips-1-theres-a-git-config-for-that/git-blame-with-following)
  * [Word Diff](#word-diff)
  * [Resolution Reuse](#reuse-recorded-resolution)

  Let's dig in!

  ## Conditional Configs

  Many of you probably know this, but Git has a cool little key/value store called `git config` which will check in three places for values to use when it's running various commands.

  Every Git user will have [probably been told](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup?ref=blog.gitbutler.com) to run something like this when they first set up:

  ```routeros
  $ git config --global user.name "John Doe"
  $ git config --global user.email johndoe@example.com
  ```

  That adds the `user.name` value to your `~.gitconfig` file. ==But there is also== `==--system==` ==(which probably none of you have used) which writes it to a system-wide config file and== `==--local==` ==(the default) that writes it to== `==.git/====config==` ==in whatever project you're currently in.==

  When Git looks for a value, it will look in that order - local, global, system - for a definition.

  ==However, there is a== _==secret fourth==_ ==place that Git can look. If you add to your global config something that looks like this:==

  ```cs
  [includeIf "gitdir:~/projects/oss"]
      path = ~/.gitconfig-oss
  ```

  ==Then Git will look in the== `==~/.gitconfig-oss==` ==files for values== _==only if==_ ==the project you are currently working in matches== `==~====/projects/====oss==` ==. So, you could have a "work" directory and have work-specific values there (company email address, gpg signing key, etc) and an "oss" directory with values for your open source projects, etc.==

  But `gitdir` is not the only filter you can use. You can also put _branch name_ specific values as a include filter with `onbranch` or you can only include config files if the project you are currently in has a remote matching a specific URL with `hasconfig:remote.*.url` . So like if you have GitHub org specific keys or something.

  Check out [the docs](https://git-scm.com/docs/git-config?ref=blog.gitbutler.com#%5Fincludes) for more.

  ## Git Blame and Log with Line Ranges

  There are a couple of interesting options that you can use with `git blame` that most people don't know about and nearly none of the existing GUIs implement.

  One is the line range option, `-L`. A lot of times, if you're running blame on the command line, you just page the whole file and find the part you're looking for. However, if you want to just display a subsection of your file, you can give it a line range, like `git blame -L 28,43 path/to/file`

  ![](https://proxy-prod.omnivore-image-cache.app/2000x521,s-gI7WBL754ILzJIrAX--1643Ur5JnSBRuJhH1OIoSVk/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-06-at-15.17.13@2x.png)

  git blame -L

  You can also use a semi-strange `:` syntax to give Git a pattern to find the beginning of a block and only blame that block. So in this same situation, I can get the same result by running `git blame -L :'class LocalFile' gitbutler-ui/src/lib/vbranches/types.ts`

  Typically you can use a function name or something for that string.

  The _other_ thing you can do to see similar information in a different way, is to run `git log` with similar options. This will give you all the commits filtered to those that last touched this region of the file. So for example, `git log -L28,43:gitbutler-ui/src/lib/vbranches/types.ts` will give you something like this:

  ![](https://proxy-prod.omnivore-image-cache.app/1330x1554,s1UJYnXDDdzZCHCtQklZ7-TjA6hDYVdPC5VbLP8liK9s/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-06-at-16.42.55@2x.png)

  So instead of being ordered by lines, it sort of gathers all the commits that are shown in the blame and then shows you those commits with code that modified that block in each commit. Basically the same data, but in a different format, more like a story of how that code was put together.

  ## Git Blame with Following

  ==One thing that is really not great about using blame in GUI tools is that the CLI has much more powerful tooling for finding something closer to the real story behind your code.==

  There are many scenarios where this is really valuable. The first is ignoring whitespace changes. Some of the GUIs do that, but not all of them. So if you go and implement a `prettierrc` file, BLAM, now you're the owner of tons of lines of code. The `git blame -w` option will ignore these types of whitespace changes.

  The other great option is `-C` which will look for code movement between files in a commit. So if you refactor a function from one file to another, the normal `git blame` will simply show you as the author in the new file, but the `-C` option will follow that movement and show the last person to actually change those lines of code. Either of these scenarios could be what you're looking for, but I would argue that more often it's the latter.

  If you want Git to try even harder (look for movement in multiple commits or in _all_ of your commits) you can pass `-C` up to three times.

   Also, your GUI _does not_ do this (most likely, I can't speak for all of them).

  So, let's look at the VS Code GitLens blame output of the previous example:

  ![](https://proxy-prod.omnivore-image-cache.app/2000x592,sJ273c2NDlCitPAhifHX1YOXueBGDaERpAceyt8mJCaY/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-06-at-15.17.36@2x.png)

  git blame in GitLens VS Code plugin

  Ok, looks good. Kiril wrote most of this code it appears. Let's now look at the same block with `git blame -w -C -C -C`

  ![](https://proxy-prod.omnivore-image-cache.app/2000x502,syC33t00Kd2IdxqHirm_tueemYzBlfSNjnzjwqhanPd4/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-06-at-15.17.01@2x.png)

  git blame -C -C -C

  Now we can see that Git has followed this hunk of code from file to file over the course of multiple renames. 

  Also, Kiril only really owns a few lines of code, Mattias actually wrote big hunks of it. If we want to know about those lines, it's much better to ask Mattias rather than Kiril, as our GUI blame would suggest. 

  ## Word Diff

  This is incredibly minor, and some GUIs have nice versions of this (I find GitHub's better than what I'm about to show you, since it subtly does both) but if you _ARE_ running `git diff` on the command line and there is a line change where something small changed within it, you can change Git's default format to word-based rather than line based with the `--word-diff` option.

  ![](https://proxy-prod.omnivore-image-cache.app/1486x404,sqppS5EQzYWWKi-Y-3i2K_-fL090EekLjppG43Mst2qA/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-08.19.47@2x.png)

  normal, line-based git diff

  ![](https://proxy-prod.omnivore-image-cache.app/1428x628,sNHm1bxMfK2e5r2nox_blv7vaKHj-6Lf3FOTYgeOkya8/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-08.19.28@2x.png)

  super cool git diff --word-diff

  ## Reuse Recorded Resolution

  ==Finally, if you're rebasing or cherry-picking a lot and you've ever run into the same conflict more than once, you can turn on a feature where Git memorizes the conflict and the resolution to it. If it ever sees that same conflict again, it will== _==automatically==_ ==re-solve it for you.==

  ==You can easily turn it on with the config setting== `==rerere====.enabled==` ==and you can further ask it to automatically stage it for you with== `==rerere.====auto====Update==`

  ```routeros
  $ git config --global rerere.enabled true
  $ git config --global rerere.autoUpdate true
  ```

  ![](https://proxy-prod.omnivore-image-cache.app/1318x294,sX3LU-meJf_hU5ed2UJyHDxUFbVHUmP8ABem5jqgxwn4/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-08.21.07@2x.png)

  a conflict... always remembers

  Then, the next time you get a merge conflict that it's seen before, magic!

  ![](https://proxy-prod.omnivore-image-cache.app/1318x286,sW3jEfpz3Hik-ekQ2xOm_Vujx2Vk8MKKYqniW9WP3wo8/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-08.23.20@2x.png)

  automatically fix it the next time

  ## Up Next

  Again, all of this has been in Git for a while, but if you don't know... now you know. 

  Next up is [New Stuff in Git](https://blog.gitbutler.com/git-tips-2-new-stuff-in-git/).

  ### Subscribe to new posts.

metadata:
  {"date_added":"2024-02-18 11:02:02","id":"a6c66f79-fd38-4d76-b05a-9c5d7dc9119f","status":"ARCHIVED","tags":["read-later","dotfiles","git"],"title":"Git Tips 1: Oldies but Goodies\n","url_omnivore":"https://omnivore.app/me/git-tips-1-oldies-but-goodies-18dbc861eae\n","url_original":"https://blog.gitbutler.com/git-tips-1-theres-a-git-config-for-that/\n"}


path: Omnivore/2024-02-18 - Git Tips 2- New Stuff in Git.md
sortable_path: Omnivore2024-02-18 - Git Tips 2- New Stuff in Git.md
title: Git Tips 2: New Stuff in Git
lead: # Git Tips 2: New Stuff in Git
checksum: 68523104e0e060b083098351ea9669d86dfd768ac9da623ad7b510bed90b6e00
created: 2024-09-15 00:40:58.067035407+00:00
modified: 2024-09-22 22:40:13.395511052+00:00
body:
  # Git Tips 2: New Stuff in Git

  ## Highlights

  So, Git has created a new force pushing option called `--force-with-lease` that will essentially check that what you last pushed is still what's on the server before it will force the new branch update. 

  [source](https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77#303433c7-6c8d-45df-b8ae-729aa598255b) 

  ---

  It's pretty easy to do. Just set `gpg.format` to `ssh` and tell it where your signing key is:

  ```routeros
  $ git config gpg.format ssh
  $ git config user.signingKey ~/.ssh/id_rsa.pub
  ```

  Now if you run `git commit -S` it will try to sign your commit with this key. If it succeeds and you upload that public key to GitHub here (under "Signing Keys"), then you'll get pretty "verified" badges on your commits: 

  [source](https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77#6870b9f8-0e0c-4a26-8d4e-b0dd630f7260) 

  ---

  provides a way to add cronjobs that run daily, hourly and weekly maintenance tasks on your Git repositories.

  You can turn it on for your Git repository by simply running:

  ```crmsh
  $ git maintenance start
  ``` 

  [source](https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77#0a2d2271-21f5-46e5-8f83-8fd08a82e25c) 

  ---

  This means that every hour it will rebuild your commit graph and do a prefetch (we will cover these concepts in the next post), and once per day it will clean up loose objects and put them in pack-files and also repack the object directory using the `multi-pack-index` feature (read more about that in an incredible blog post from GitHub's Taylor Blau [here](https://github.blog/2021-04-29-scaling-monorepo-maintenance/?ref=blog.gitbutler.com#multi-pack-indexes)).

  Basically it will just make lots of things faster in the background all the time automatically. 

  [source](https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77#cdd9aacf-451b-4437-9976-61dcbc1322aa) 

  ---

  ## Original

  ![Git Tips 2: New Stuff in Git](https://proxy-prod.omnivore-image-cache.app/0x0,sF_8YxOLhNs5ye8q29KVmP2HYhguriXQ9k9yHwAsrFhg/https://blog.gitbutler.com/content/images/size/w500/2024/02/CleanShot-2024-02-08-at-15.55.24@2x.png) 

   There are a bunch of new tricks that Git can do that were added in the last few years. How up to date are you?

  Next up in our [3 part series](https://blog.gitbutler.com/git-tips-and-tricks/) on "Stuff you may not know about Git", we have **New Stuff**!

  Here I'm going to cover 5 relatively new things in Git that you may not have heard about, because _why would you_?

  We'll cover:

  * [Git Branch Stuff](#some-git-branch-stuff)
  * [Safe Force Pushing](#safe-force-pushing)
  * [SSH Commit Signing](#ssh-commit-signing)
  * [Push Signing](#push-signing)
  * [Git Maintenance](#git-maintenance)

  Let's dig in!

  ## Some Git Branch Stuff

  This is pretty minor, but one thing that's always bugged me about Git is that I run `git branch` a lot to view what branches I have, but they're in the dumbest possible order (alphabetic) and there are a million of them after a while.

  At some point I started naming my branches in a way to partially cope with this. Every branch would be something like `sc-0831-my-thing` meaning that the branch topic was "my thing", it was created on August 31st and the `sc` are my initials so I can group them by whose branch. It's a lot of stupid metadata to try to cram into a branch name just because of how it's listed.

  However, now we can ask Git to do two things that help with this. We can ask it to sort by `objectsize`, `authordate`, `committerdate`, `creatordate`, or `taggerdate` with the `--sort` option and we can set it as a default with the `branch.sort` config setting.

  So for example, if I want to sort by last commit date descending, I can run:

  ```routeros
  $ git config --global branch.sort -committerdate
  ```

  And now the default will show the branch that I last committed to at the top. 

  💡

  Important note: the `-committerdate` has a leading `-` but __not_ a double dash. It's just a negative. I've seen people mess this up and then things break.

  However, now if I have a bunch of branches, that will scroll off the screen. Sad. But now Git also has a way to take a list of branches and try to split it into columns to make better use of the screen real estate. You can do this either with the new `--column` option, or with the `column.ui` setting.

  Check it out:

  ![](https://proxy-prod.omnivore-image-cache.app/2000x1124,sqLOqa3Y2phwi1xtcXNMFJVLI70rNK_SePOKKJItmLlU/https://blog.gitbutler.com/content/images/2024/02/image-1.png)

  Nice sorted columns for my branch output

  As another sort of funny thing, in order to help with this, Git implemented it's own list to column terminal command that is sort of completely independent of anything else in Git and is it's own command called `git column`. 

  ![](https://proxy-prod.omnivore-image-cache.app/2000x1125,sF46gm3RYU0kuv5FXNGOjwci1KTyAyEyftNPkRV8gZfg/https://blog.gitbutler.com/content/images/2024/02/image-2.png)

  Just in case there is anything else you need to convert into columns that isn't Git related.

  ## Safe Force Pushing

  The next interesting thing that Git has added somewhat recently is a way to do much safer forced pushes.

  Generally most of us don't love doing forced pushes, because there is always a chance that you're overwriting someone else's commits. Let's take a scenario:

  * You commit and push something to GitHub
  * Someone else pulls it down, commits something and pushes it back up.
  * You amend a commit, rewriting the history, and force push it, not knowing that anyone had based something off your work.
  * This effectively removes what the other person had done.

  What you really want to do is check to see if anyone else had pushed and only force push if the answer is no. However, there is always a bit of a race condition here because even if you check first, in the second it takes you to then push, something else could have landed from elsewhere in the meantime.

  ==So, Git has created a new force pushing option called== `==--====force====-====with====-lease==` ==that will essentially check that what you last pushed is still what's on the server before it will force the new branch update.==

  ![](https://proxy-prod.omnivore-image-cache.app/674x132,s3pd3boc6BtJmq1-zdkMXpkKuEGQno46qKULKU0RWmQM/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-10.15.11@2x.png)

  A failed --force-with-lease push

  If someone has updated the remote ref (pushed in the meantime), then push now fails with a "stale info" error.

  If you're amending and rebasing stuff a lot, it may be worth setting up a nice little alias for this, because it's almost _always_ better than running `--force`

  ```routeros
  $ git config --global alias.fpush push --force-with-lease
  ```

  May the force be with you.

  ## Commit Signing with SSH

  We [wrote about this](https://blog.gitbutler.com/signing-commits-in-git-explained/) a few months ago in mind-numbing detail, because the GitButler client does this automatically for you with the flip of a config setting, but if you want to do this on the command line, read on.

  Git has supported signing your commits with GPG for a while, but GPG is often pretty difficult to get working properly and completely understand if you've never used it before. Recently, OpenSSH provided a new way to sign data using your existing SSH key and Git has integrated this as an option to use instead of GPG to do the same thing. Also, importantly, GitHub and GitLab support verifying these signatures if you upload your public signing key to your user account there.

  ==It's pretty easy to do. Just set== `==gpg====.format==` ==to== `==ssh==` ==and tell it where your signing key is:==

  ```routeros
  $ git config gpg.format ssh
  $ git config user.signingKey ~/.ssh/id_rsa.pub
  ```

  ==Now if you run== `==git commit -S==` ==it will try to sign your commit with this key. If it succeeds and you upload that public key to GitHub here (under "Signing Keys"), then you'll get pretty "verified" badges on your commits:==

  ![](https://proxy-prod.omnivore-image-cache.app/2000x500,sKtX9g8YhqvINuvCorwsyVI4voymwk0MnSrd-FI_pCb8/https://blog.gitbutler.com/content/images/2024/02/s_E036A4CDB1CAE14FC00AF40FA13C8C8B49781C4FDF6B604EA9B3BFCD9F34B628_1695546510881_CleanShot-2023-09-24-at-11.08.142x.png)

  Stay vigilant.

  ## Push Signing

  I won't go into a ton of detail here because this isn't really widely used, but it might be interesting to some. Git can also now sign _pushes_, not just commits. 

  Since none of the major Git hosting solutions (GitHub, GitLab, Bitbucket) support this, it's only really possible to do this if you run your own server. However, if you do, you can run `git push --signed` in order to sign the ref update on the server and have the server save a transparency log with verifiable signatures somewhere.

  If you're interested in this, there is a very nice [writeup](https://people.kernel.org/monsieuricon/signed-git-pushes?ref=blog.gitbutler.com) by over at kernel.org.

  Push it real good.

  ## Git Maintenance

  The final fun new thing I'll cover is `git maintenance`.

  The maintenance command was introduced in Git 2.30 I believe. It essentially ==provides a way to add cronjobs that run daily, hourly and weekly maintenance tasks on your Git repositories.==

  ==You can turn it on for your Git repository by simply running:==

  ```crmsh
  $ git maintenance start
  ```

  This will modify your `.git/config` file to add a `maintenance.strategy` value set to `incremental` which is a shorthand for the following values:

  * `gc`: disabled.
  * `commit-graph`: hourly.
  * `prefetch`: hourly.
  * `loose-objects`: daily.
  * `incremental-repack`: daily.

  ==This means that every hour it will rebuild your commit graph and do a prefetch (we will cover these concepts in the next post), and once per day it will clean up loose objects and put them in pack-files and also repack the object directory using the== `==multi-====pack====-====index==` ==feature (read more about that in an incredible blog post from GitHub's Taylor Blau== ==[here](https://github.blog/2021-04-29-scaling-monorepo-maintenance/?ref=blog.gitbutler.com#multi-pack-indexes)====).==

  ==Basically it will just make lots of things faster in the background all the time automatically.==

  Git maintenance will schedule these cron jobs differently depending on the operating system. On Mac it will add some LaunchAgents like this:

  ![](https://proxy-prod.omnivore-image-cache.app/1950x1180,sDgXunpWspBACKspfJeXdbbBlnTVlZ9qqNhma8sMspeI/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-10.52.26@2x.png)

  If you're curious what these plist files look like, it's something like this:

  ![](https://proxy-prod.omnivore-image-cache.app/2000x1201,sutSoVq9csWlbPbrG3dZPjQ6roru0PmhM-Y5CjVgzgg8/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-10.52.02@2x.png)

  You can read more about git maintenance and it's various options [here](https://git-scm.com/docs/git-maintenance?ref=blog.gitbutler.com).

  OK, now onto our next post where we cover those commit graph and prefetching topics. Let's get into [Really Big Repositories](https://blog.gitbutler.com/git-tips-3-really-large-repositories/).

  ### Subscribe to new posts.

raw_content:
  ---
  id: 3811e8c9-49ed-47bc-8302-d7dc0529d828
  title: |
    Git Tips 2: New Stuff in Git
  status: ARCHIVED
  tags:
    - read-later
    - dotfiles
    - git
  date_added: 2024-02-18 11:02:19
  url_omnivore: |
    https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77
  url_original: |
    https://blog.gitbutler.com/git-tips-2-new-stuff-in-git/
  ---

  # Git Tips 2: New Stuff in Git

  ## Highlights

  So, Git has created a new force pushing option called `--force-with-lease` that will essentially check that what you last pushed is still what's on the server before it will force the new branch update. 

  [source](https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77#303433c7-6c8d-45df-b8ae-729aa598255b) 

  ---

  It's pretty easy to do. Just set `gpg.format` to `ssh` and tell it where your signing key is:

  ```routeros
  $ git config gpg.format ssh
  $ git config user.signingKey ~/.ssh/id_rsa.pub
  ```

  Now if you run `git commit -S` it will try to sign your commit with this key. If it succeeds and you upload that public key to GitHub here (under "Signing Keys"), then you'll get pretty "verified" badges on your commits: 

  [source](https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77#6870b9f8-0e0c-4a26-8d4e-b0dd630f7260) 

  ---

  provides a way to add cronjobs that run daily, hourly and weekly maintenance tasks on your Git repositories.

  You can turn it on for your Git repository by simply running:

  ```crmsh
  $ git maintenance start
  ``` 

  [source](https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77#0a2d2271-21f5-46e5-8f83-8fd08a82e25c) 

  ---

  This means that every hour it will rebuild your commit graph and do a prefetch (we will cover these concepts in the next post), and once per day it will clean up loose objects and put them in pack-files and also repack the object directory using the `multi-pack-index` feature (read more about that in an incredible blog post from GitHub's Taylor Blau [here](https://github.blog/2021-04-29-scaling-monorepo-maintenance/?ref=blog.gitbutler.com#multi-pack-indexes)).

  Basically it will just make lots of things faster in the background all the time automatically. 

  [source](https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77#cdd9aacf-451b-4437-9976-61dcbc1322aa) 

  ---

  ## Original

  ![Git Tips 2: New Stuff in Git](https://proxy-prod.omnivore-image-cache.app/0x0,sF_8YxOLhNs5ye8q29KVmP2HYhguriXQ9k9yHwAsrFhg/https://blog.gitbutler.com/content/images/size/w500/2024/02/CleanShot-2024-02-08-at-15.55.24@2x.png) 

   There are a bunch of new tricks that Git can do that were added in the last few years. How up to date are you?

  Next up in our [3 part series](https://blog.gitbutler.com/git-tips-and-tricks/) on "Stuff you may not know about Git", we have **New Stuff**!

  Here I'm going to cover 5 relatively new things in Git that you may not have heard about, because _why would you_?

  We'll cover:

  * [Git Branch Stuff](#some-git-branch-stuff)
  * [Safe Force Pushing](#safe-force-pushing)
  * [SSH Commit Signing](#ssh-commit-signing)
  * [Push Signing](#push-signing)
  * [Git Maintenance](#git-maintenance)

  Let's dig in!

  ## Some Git Branch Stuff

  This is pretty minor, but one thing that's always bugged me about Git is that I run `git branch` a lot to view what branches I have, but they're in the dumbest possible order (alphabetic) and there are a million of them after a while.

  At some point I started naming my branches in a way to partially cope with this. Every branch would be something like `sc-0831-my-thing` meaning that the branch topic was "my thing", it was created on August 31st and the `sc` are my initials so I can group them by whose branch. It's a lot of stupid metadata to try to cram into a branch name just because of how it's listed.

  However, now we can ask Git to do two things that help with this. We can ask it to sort by `objectsize`, `authordate`, `committerdate`, `creatordate`, or `taggerdate` with the `--sort` option and we can set it as a default with the `branch.sort` config setting.

  So for example, if I want to sort by last commit date descending, I can run:

  ```routeros
  $ git config --global branch.sort -committerdate
  ```

  And now the default will show the branch that I last committed to at the top. 

  💡

  Important note: the `-committerdate` has a leading `-` but __not_ a double dash. It's just a negative. I've seen people mess this up and then things break.

  However, now if I have a bunch of branches, that will scroll off the screen. Sad. But now Git also has a way to take a list of branches and try to split it into columns to make better use of the screen real estate. You can do this either with the new `--column` option, or with the `column.ui` setting.

  Check it out:

  ![](https://proxy-prod.omnivore-image-cache.app/2000x1124,sqLOqa3Y2phwi1xtcXNMFJVLI70rNK_SePOKKJItmLlU/https://blog.gitbutler.com/content/images/2024/02/image-1.png)

  Nice sorted columns for my branch output

  As another sort of funny thing, in order to help with this, Git implemented it's own list to column terminal command that is sort of completely independent of anything else in Git and is it's own command called `git column`. 

  ![](https://proxy-prod.omnivore-image-cache.app/2000x1125,sF46gm3RYU0kuv5FXNGOjwci1KTyAyEyftNPkRV8gZfg/https://blog.gitbutler.com/content/images/2024/02/image-2.png)

  Just in case there is anything else you need to convert into columns that isn't Git related.

  ## Safe Force Pushing

  The next interesting thing that Git has added somewhat recently is a way to do much safer forced pushes.

  Generally most of us don't love doing forced pushes, because there is always a chance that you're overwriting someone else's commits. Let's take a scenario:

  * You commit and push something to GitHub
  * Someone else pulls it down, commits something and pushes it back up.
  * You amend a commit, rewriting the history, and force push it, not knowing that anyone had based something off your work.
  * This effectively removes what the other person had done.

  What you really want to do is check to see if anyone else had pushed and only force push if the answer is no. However, there is always a bit of a race condition here because even if you check first, in the second it takes you to then push, something else could have landed from elsewhere in the meantime.

  ==So, Git has created a new force pushing option called== `==--====force====-====with====-lease==` ==that will essentially check that what you last pushed is still what's on the server before it will force the new branch update.==

  ![](https://proxy-prod.omnivore-image-cache.app/674x132,s3pd3boc6BtJmq1-zdkMXpkKuEGQno46qKULKU0RWmQM/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-10.15.11@2x.png)

  A failed --force-with-lease push

  If someone has updated the remote ref (pushed in the meantime), then push now fails with a "stale info" error.

  If you're amending and rebasing stuff a lot, it may be worth setting up a nice little alias for this, because it's almost _always_ better than running `--force`

  ```routeros
  $ git config --global alias.fpush push --force-with-lease
  ```

  May the force be with you.

  ## Commit Signing with SSH

  We [wrote about this](https://blog.gitbutler.com/signing-commits-in-git-explained/) a few months ago in mind-numbing detail, because the GitButler client does this automatically for you with the flip of a config setting, but if you want to do this on the command line, read on.

  Git has supported signing your commits with GPG for a while, but GPG is often pretty difficult to get working properly and completely understand if you've never used it before. Recently, OpenSSH provided a new way to sign data using your existing SSH key and Git has integrated this as an option to use instead of GPG to do the same thing. Also, importantly, GitHub and GitLab support verifying these signatures if you upload your public signing key to your user account there.

  ==It's pretty easy to do. Just set== `==gpg====.format==` ==to== `==ssh==` ==and tell it where your signing key is:==

  ```routeros
  $ git config gpg.format ssh
  $ git config user.signingKey ~/.ssh/id_rsa.pub
  ```

  ==Now if you run== `==git commit -S==` ==it will try to sign your commit with this key. If it succeeds and you upload that public key to GitHub here (under "Signing Keys"), then you'll get pretty "verified" badges on your commits:==

  ![](https://proxy-prod.omnivore-image-cache.app/2000x500,sKtX9g8YhqvINuvCorwsyVI4voymwk0MnSrd-FI_pCb8/https://blog.gitbutler.com/content/images/2024/02/s_E036A4CDB1CAE14FC00AF40FA13C8C8B49781C4FDF6B604EA9B3BFCD9F34B628_1695546510881_CleanShot-2023-09-24-at-11.08.142x.png)

  Stay vigilant.

  ## Push Signing

  I won't go into a ton of detail here because this isn't really widely used, but it might be interesting to some. Git can also now sign _pushes_, not just commits. 

  Since none of the major Git hosting solutions (GitHub, GitLab, Bitbucket) support this, it's only really possible to do this if you run your own server. However, if you do, you can run `git push --signed` in order to sign the ref update on the server and have the server save a transparency log with verifiable signatures somewhere.

  If you're interested in this, there is a very nice [writeup](https://people.kernel.org/monsieuricon/signed-git-pushes?ref=blog.gitbutler.com) by over at kernel.org.

  Push it real good.

  ## Git Maintenance

  The final fun new thing I'll cover is `git maintenance`.

  The maintenance command was introduced in Git 2.30 I believe. It essentially ==provides a way to add cronjobs that run daily, hourly and weekly maintenance tasks on your Git repositories.==

  ==You can turn it on for your Git repository by simply running:==

  ```crmsh
  $ git maintenance start
  ```

  This will modify your `.git/config` file to add a `maintenance.strategy` value set to `incremental` which is a shorthand for the following values:

  * `gc`: disabled.
  * `commit-graph`: hourly.
  * `prefetch`: hourly.
  * `loose-objects`: daily.
  * `incremental-repack`: daily.

  ==This means that every hour it will rebuild your commit graph and do a prefetch (we will cover these concepts in the next post), and once per day it will clean up loose objects and put them in pack-files and also repack the object directory using the== `==multi-====pack====-====index==` ==feature (read more about that in an incredible blog post from GitHub's Taylor Blau== ==[here](https://github.blog/2021-04-29-scaling-monorepo-maintenance/?ref=blog.gitbutler.com#multi-pack-indexes)====).==

  ==Basically it will just make lots of things faster in the background all the time automatically.==

  Git maintenance will schedule these cron jobs differently depending on the operating system. On Mac it will add some LaunchAgents like this:

  ![](https://proxy-prod.omnivore-image-cache.app/1950x1180,sDgXunpWspBACKspfJeXdbbBlnTVlZ9qqNhma8sMspeI/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-10.52.26@2x.png)

  If you're curious what these plist files look like, it's something like this:

  ![](https://proxy-prod.omnivore-image-cache.app/2000x1201,sutSoVq9csWlbPbrG3dZPjQ6roru0PmhM-Y5CjVgzgg8/https://blog.gitbutler.com/content/images/2024/02/CleanShot-2024-02-08-at-10.52.02@2x.png)

  You can read more about git maintenance and it's various options [here](https://git-scm.com/docs/git-maintenance?ref=blog.gitbutler.com).

  OK, now onto our next post where we cover those commit graph and prefetching topics. Let's get into [Really Big Repositories](https://blog.gitbutler.com/git-tips-3-really-large-repositories/).

  ### Subscribe to new posts.

metadata:
  {"date_added":"2024-02-18 11:02:19","id":"3811e8c9-49ed-47bc-8302-d7dc0529d828","status":"ARCHIVED","tags":["read-later","dotfiles","git"],"title":"Git Tips 2: New Stuff in Git\n","url_omnivore":"https://omnivore.app/me/git-tips-2-new-stuff-in-git-18dbc865f77\n","url_original":"https://blog.gitbutler.com/git-tips-2-new-stuff-in-git/\n"}


