Ir al contenido
angel.library

Prettier

Prettier es un formateador con opinión: recibe el código, descarta el formato original y lo reimprime siguiendo sus propias reglas. No analiza lógica, no detecta errores y no cambia el comportamiento del programa — solo decide dónde van los saltos de línea, las comillas y la indentación.

Esa distinción importa porque define el reparto de responsabilidades con ESLint: Prettier decide cómo se ve el código, ESLint decide si el código tiene problemas. Cuando ambos intentan controlar la misma regla de estilo aparecen conflictos y reformateos en bucle al guardar.

La base mínima es el propio Prettier:

Ventana de terminal
pnpm add -D prettier

Con los tres plugins que se documentan más abajo:

Ventana de terminal
pnpm add -D prettier @trivago/prettier-plugin-sort-imports prettier-plugin-tailwindcss prettier-plugin-astro

Prettier se instala siempre como dependencia de desarrollo: es una herramienta del entorno de trabajo, no forma parte de lo que se ejecuta en producción.

Añade los scripts al package.json:

package.json
{
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check ."
}
}

--write reescribe los archivos; --check solo informa cuáles no cumplen el formato y termina con código de salida distinto de cero. Usa --check en integración continua: falla el pipeline sin modificar el repositorio.

Prettier por sí solo no ordena imports ni entiende .astro. Cada capacidad extra llega como plugin declarado en el campo plugins.

PluginQué aportaObligatorio para
@trivago/prettier-plugin-sort-importsOrdena y agrupa los import según patrones que tú definesQue importOrder tenga efecto
prettier-plugin-tailwindcssOrdena las clases de Tailwind en el orden canónico del frameworkDiffs estables en componentes
prettier-plugin-astroEnseña a Prettier a leer y formatear archivos .astroProyectos Astro

Este es el plugin que resuelve el desorden real de un proyecto: sin él, cada archivo termina con los import en el orden accidental en que se fueron agregando.

El punto crítico: los campos importOrder, importOrderSeparation e importOrderSortSpecifiers no son opciones nativas de Prettier. Si declaras esos campos sin instalar y registrar el plugin, Prettier los ignora en silencio — el archivo se ve configurado y no ordena nada.

importOrder recibe un arreglo de expresiones regulares que se evalúan en orden contra la ruta de cada import. El primer patrón que coincide determina el grupo, y los grupos se imprimen en el orden del arreglo:

.prettierrc
{
"importOrder": [
"^react$",
"^react/(.*)$",
"^next$",
"^next/(.*)$",
"<THIRD_PARTY_MODULES>",
"^@/(.*)$",
"^(?!.*\\.css$)[./]",
"^.+\\.css$"
]
}
PatrónQué capturaEjemplo
^react$El paquete exacto, sin subrutasimport React from "react"
^react/(.*)$Solo las subrutas del paqueteimport { createRoot } from "react-dom/client"
<THIRD_PARTY_MODULES>Comodín del plugin: todo lo de node_modules que no coincidió antesimport clsx from "clsx"
^@/(.*)$El alias interno del proyectoimport { SITE } from "@/config/site"
^(?!.*\\.css$)[./]Rutas relativas que no terminan en .cssimport { Card } from "./Card"
^.+\\.css$Cualquier import de hoja de estilosimport "./globals.css"

El resultado es un orden que va de lo más externo a lo más local: framework, paquetes de terceros, código propio por alias, código propio relativo y estilos al final.

Dos detalles que se pasan por alto:

  • Los estilos van últimos a propósito. En CSS el orden de importación afecta la cascada, así que mover un import "./globals.css" puede cambiar qué regla gana. El patrón con negative lookahead ((?!.*\.css$)) existe justamente para que las hojas de estilo queden fuera del grupo de rutas relativas y no se mezclen.
  • El orden del arreglo importa más que el contenido. ^react$ debe ir antes que <THIRD_PARTY_MODULES>; si estuviera después, React caería en el comodín y nunca llegaría a su propio grupo.

Los otros dos campos del plugin:

CampoValor usadoQué hace
importOrderSeparationfalseNo inserta una línea en blanco entre cada grupo. Con true, cada grupo queda separado visualmente
importOrderSortSpecifierstrueOrdena alfabéticamente lo que va dentro de las llaves: import { a, b, c }

prettier-plugin-tailwindcss reordena las clases al orden oficial del framework, lo que hace que dos personas que escriben las mismas clases en distinto orden produzcan exactamente el mismo diff.

Debe ser siempre el último elemento del arreglo plugins. El plugin está diseñado para envolver a los demás; si otro plugin se declara después, el orden de clases deja de aplicarse.

En Tailwind CSS 4 la configuración vive en el propio CSS, así que el plugin necesita saber cuál es la hoja de entrada:

.prettierrc
{
"plugins": [
"@trivago/prettier-plugin-sort-imports",
"prettier-plugin-tailwindcss"
],
"tailwindStylesheet": "./src/styles/global.css"
}

tailwindStylesheet es el reemplazo de tailwindConfig, que apuntaba al tailwind.config.js de Tailwind 3.

Estos son los campos nativos de Prettier. La columna Por defecto indica el valor que Prettier usa cuando no declaras nada, para que puedas distinguir qué estás cambiando de verdad.

CampoPor defectoQué hace
semitrueTermina cada sentencia con punto y coma. Con false, Prettier los omite y añade un ; inicial solo en las líneas que serían ambiguas
singleQuotefalseComillas simples en JavaScript y TypeScript. false mantiene dobles
jsxSingleQuotefalseLo mismo para los atributos JSX, que se configura por separado
quoteProps"as-needed"Cuándo poner comillas en las claves de objeto. "preserve" respeta lo que ya escribiste; "as-needed" las quita cuando no hacen falta
trailingComma"all"Coma final en listas multilínea. "none" no la pone; "all" la incluye incluso en parámetros de función

trailingComma: "all" produce diffs más limpios (agregar un elemento toca una sola línea), pero "none" genera archivos visualmente más sobrios. Es una elección de equipo, no técnica.

CampoPor defectoQué hace
printWidth80Ancho objetivo de línea. Es una guía, no un límite: una cadena larga o una URL lo superan sin romperse
tabWidth2Espacios por nivel de indentación. Debe coincidir con editor.tabSize y con .editorconfig
useTabsfalseIndenta con espacios. Con true usa tabulaciones reales
bracketSpacingtrueEspacios dentro de las llaves de objeto: { name } frente a {name}
arrowParens"always"Paréntesis en el parámetro único de una función flecha: (x) => x frente a x => x
CampoPor defectoQué hace
bracketSameLinefalseColoca el > de una etiqueta multilínea en su propia línea
singleAttributePerLinefalseUn atributo por línea cuando el elemento se parte. Con true, los componentes con muchas props quedan en columna y los diffs señalan el atributo exacto que cambió
htmlWhitespaceSensitivity"css"Respeta el display que CSS aplicaría al decidir dónde puede insertar saltos. Evita cambiar el renderizado de texto en línea
vueIndentScriptAndStylefalseIndenta el contenido de <script> y <style> en archivos Vue
embeddedLanguageFormatting"auto"Formatea el código incrustado en otro lenguaje (CSS dentro de HTML, SQL en template literals) cuando lo reconoce
CampoPor defectoQué hace
proseWrap"preserve"Conserva los saltos de línea que escribiste en Markdown. "always" los reajusta a printWidth
insertPragmafalseAñade un comentario @format en los archivos que formatea
requirePragmafalseFormatea únicamente archivos que ya tengan el comentario @format. Sirve para adoptar Prettier gradualmente en un proyecto grande
experimentalTernariesfalseFormato experimental para ternarios anidados

En Markdown, proseWrap: "preserve" es casi siempre la opción correcta: los saltos de línea de un texto suelen ser una decisión editorial, y "always" los reescribiría.

El campo overrides permite aplicar reglas distintas a un conjunto de archivos sin partir la configuración en varios archivos:

.prettierrc
{
"overrides": [
{
"files": "*.md",
"options": { "printWidth": 100 }
}
]
}

En VS Code, la elección del formateador por lenguaje se hace del lado del editor, no de Prettier:

settings.json
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"[astro]": {
"editor.defaultFormatter": "astro-build.astro-vscode"
}
}

.prettierignore usa la misma sintaxis que .gitignore y excluye rutas del formateo. Es tan importante como el .prettierrc: sin él, prettier --write . recorre carpetas generadas y puede tardar minutos o modificar archivos que no deberían tocarse.

.prettierignore
.next
.astro
.vercel
build
coverage
dist
node_modules
bun.lock
bun.lockb
package-lock.json
pnpm-lock.yaml

Qué se excluye y por qué:

EntradaMotivo
node_modulesCódigo de terceros. Formatearlo no aporta nada y se pierde en la siguiente instalación
.next, .astro, .vercel, dist, build, outArtefactos de compilación. Se regeneran en cada build
coverageReportes generados por el runner de tests
LockfilesLos genera el gestor de paquetes con su propio formato. Reformatearlos produce diffs enormes y puede confundir a la herramienta

Del bloque de lockfiles, solo debería existir en el repositorio el del gestor que realmente usas. Listarlos todos en .prettierignore es una precaución barata; tener los tres archivos versionados sí es un problema.

Ambos pueden opinar sobre el mismo código, y ahí está el conflicto habitual: el archivo se reformatea al guardar y ESLint lo marca como error, o al revés.

La regla práctica: un solo responsable del formato. Prettier se encarga del estilo; ESLint queda para reglas que analizan el código (variables sin usar, hooks mal llamados, imports inexistentes).

En VS Code eso se traduce en que Prettier formatea y ESLint corrige aparte:

settings.json
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}

Formatear al guardar es una comodidad local, no una comprobación. Quien decide si el repositorio cumple el formato es format:check en CI: la máquina de un colaborador puede no tener la extensión instalada.

Prettier busca su configuración subiendo desde el archivo que va a formatear hasta la raíz del proyecto, y se detiene en la primera que encuentra. Si algo no aplica como esperas, revisa en este orden:

  1. Que exista un .prettierrc (o prettier.config.js, o la clave prettier en package.json) y que sea JSON válido.
  2. Que el plugin que aporta el campo esté instalado y declarado en plugins.
  3. Que la ruta no esté cubierta por .prettierignore.
  4. Que el editor esté usando Prettier y no otro formateador para ese lenguaje.

Un .prettierrc con un error de sintaxis hace que Prettier falle o caiga a los valores por defecto, según el contexto. Si el formato cambió de golpe en todo el proyecto, ese archivo es el primer sospechoso.

Ventana de terminal
pnpm add -D prettier prettier-plugin-astro @trivago/prettier-plugin-sort-imports prettier-plugin-tailwindcss @astrojs/check typescript
.prettierrc
{
"arrowParens": "always",
"bracketSameLine": false,
"bracketSpacing": true,
"semi": false,
"experimentalTernaries": false,
"singleQuote": false,
"jsxSingleQuote": false,
"quoteProps": "preserve",
"trailingComma": "none",
"singleAttributePerLine": true,
"htmlWhitespaceSensitivity": "css",
"vueIndentScriptAndStyle": false,
"proseWrap": "preserve",
"insertPragma": false,
"printWidth": 80,
"requirePragma": false,
"tabWidth": 2,
"useTabs": false,
"embeddedLanguageFormatting": "auto",
"plugins": [
"prettier-plugin-astro",
"@trivago/prettier-plugin-sort-imports",
"prettier-plugin-tailwindcss"
],
"importOrder": [
"^astro$",
"^astro/(.*)$",
"<THIRD_PARTY_MODULES>",
"^@/(.*)/types$",
"^@/(.*)$",
"^(?!.*\\.css$)[./]",
"^.+\\.css$"
],
"importOrderSeparation": false,
"importOrderSortSpecifiers": true,
"tailwindStylesheet": "./src/styles/global.css"
}
.prettierignore
.next
.astro
.vercel
build
coverage
dist
node_modules
bun.lock
bun.lockb
package-lock.json
pnpm-lock.yaml
Ventana de terminal
pnpm add -D prettier @trivago/prettier-plugin-sort-imports prettier-plugin-tailwindcss
.prettierrc
{
"arrowParens": "always",
"bracketSameLine": false,
"bracketSpacing": true,
"semi": false,
"experimentalTernaries": false,
"singleQuote": false,
"jsxSingleQuote": false,
"quoteProps": "preserve",
"trailingComma": "none",
"singleAttributePerLine": true,
"proseWrap": "preserve",
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"embeddedLanguageFormatting": "auto",
"plugins": [
"@trivago/prettier-plugin-sort-imports",
"prettier-plugin-tailwindcss"
],
"importOrder": [
"^react$",
"^react/(.*)$",
"^next$",
"^next/(.*)$",
"<THIRD_PARTY_MODULES>",
"^@/(.*)$",
"^(?!.*\\.css$)[./]",
"^.+\\.css$"
],
"importOrderSeparation": false,
"importOrderSortSpecifiers": true,
"tailwindStylesheet": "./src/app/globals.css"
}
.prettierignore
.next
build
coverage
dist
node_modules
out
bun.lock
bun.lockb
package-lock.json
pnpm-lock.yaml