[{"data":1,"prerenderedAt":3962},["ShallowReactive",2],{"en-home-articles":3,"$f2tc3g1h18s1kk":8},[4,1073,2518,3275,3601,3789],{"id":5,"title":6,"articleCover":7,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":12,"categoriesIds":1058,"description":1061,"enAlternateSlug":1062,"extension":1063,"meta":1064,"navigation":176,"path":1065,"ptBrAlternateSlug":1066,"publishedAt":1067,"seo":1068,"sitemap":1069,"stem":1070,"updatedAt":1067,"wordCount":1071,"__hash__":1072},"blogEn\u002Fen\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it.md","Vue Vapor Mode: what it is and what changes in Vue 3.6","\u002Fimages\u002Fen\u002Fblog\u002Fvue\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it.webp",null,720,1280,"thiago-nunes-batista",{"type":13,"value":14,"toc":1032},"minimark",[15,32,39,44,47,50,53,58,88,92,103,276,283,298,302,313,383,386,390,397,454,463,467,473,525,539,542,609,620,623,627,630,692,701,708,711,718,732,767,774,778,781,897,912,916,919,926,930,933,936,953,957,961,964,968,976,980,985,989,998,1002,1005,1009,1021,1025,1028],[16,17,18,19,23,24,27,28,31],"p",{},"Vue 3.6 is not stable yet. As of October 6, 2026, the latest version is ",[20,21,22],"code",{},"3.6.0-rc.10"," (September 30), and the stable release is still ",[20,25,26],{},"3.5.43"," (September 17). I checked both on npm and in the ",[20,29,30],{},"vuejs\u002Fcore"," releases on that date.",[16,33,34,38],{},[35,36,37],"strong",{},"Vue Vapor Mode"," is the headline feature of 3.6: a compilation mode that skips the Virtual DOM and generates code that updates the DOM directly. It is opt-in, so you pick which components use it and you don't rewrite your app. This article covers how Vapor works, how to enable it in Vue and in Nuxt 4.6, what it doesn't support yet, and when it's worth a try.",[40,41,43],"h2",{"id":42},"how-vapor-works","How Vapor works",[16,45,46],{},"In the traditional mode, when reactive state changes, Vue runs the render function, builds a new tree of VNodes (plain JavaScript objects that describe the UI), compares it with the previous tree, and only then touches the real DOM.",[16,48,49],{},"In Vapor Mode, the compiler analyzes the template and generates code that manipulates the DOM directly. Vapor components don't create VNodes or diff trees at runtime. When a reactive value changes, only the part of the DOM bound to it is updated.",[16,51,52],{},"According to the official Vue 3.6 release notes, the goal is a smaller baseline bundle and better performance. The same notes say Vapor reached the same level of performance as Solid and Svelte 5 in third-party benchmarks (js-framework-benchmark). As with any benchmark, the result depends on the scenario measured.",[54,55,57],"h3",{"id":56},"what-about-alien-signals","What about Alien Signals?",[16,59,60,61,64,65,72,73,76,77,76,80,83,84,87],{},"Vue 3.6 also ships a major refactor of ",[20,62,63],{},"@vue\u002Freactivity"," based on the ",[66,67,71],"a",{"href":68,"rel":69},"https:\u002F\u002Fgithub.com\u002Fstackblitz\u002Falien-signals",[70],"nofollow","alien-signals"," library. The rc.1 notes say it improves the reactivity system's performance and memory usage. It's an internal change: ",[20,74,75],{},"ref",", ",[20,78,79],{},"reactive",[20,81,82],{},"computed"," and ",[20,85,86],{},"watch"," keep the same API. Since it's part of Vue 3.6, you get it whether or not you use Vapor.",[40,89,91],{"id":90},"how-to-enable-vapor-mode","How to enable Vapor Mode",[16,93,94,95,98,99,102],{},"Vapor works with SFCs that use ",[20,96,97],{},"\u003Cscript setup>"," or only a template. You mark the component with the ",[20,100,101],{},"vapor"," attribute:",[104,105,110],"pre",{"className":106,"code":107,"language":108,"meta":109,"style":109},"language-vue shiki shiki-themes dracula","\u003Cscript setup vapor lang=\"ts\">\nimport { ref } from 'vue'\n\nconst count = ref(0)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton @click=\"count++\">\n    count is {{ count }}\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n","vue","",[20,111,112,151,171,178,203,213,218,228,251,257,267],{"__ignoreMap":109},[113,114,117,121,125,129,132,135,138,142,146,148],"span",{"class":115,"line":116},"line",1,[113,118,120],{"class":119},"sCdxs","\u003C",[113,122,124],{"class":123},"s0Tla","script",[113,126,128],{"class":127},"sY_PY"," setup",[113,130,131],{"class":127}," vapor",[113,133,134],{"class":127}," lang",[113,136,137],{"class":123},"=",[113,139,141],{"class":140},"seVfx","\"",[113,143,145],{"class":144},"s-mGx","ts",[113,147,141],{"class":140},[113,149,150],{"class":119},">\n",[113,152,154,157,160,163,166,168],{"class":115,"line":153},2,[113,155,156],{"class":123},"import",[113,158,159],{"class":119}," { ref } ",[113,161,162],{"class":123},"from",[113,164,165],{"class":140}," '",[113,167,108],{"class":144},[113,169,170],{"class":140},"'\n",[113,172,174],{"class":115,"line":173},3,[113,175,177],{"emptyLinePlaceholder":176},true,"\n",[113,179,181,184,187,189,193,196,200],{"class":115,"line":180},4,[113,182,183],{"class":123},"const",[113,185,186],{"class":119}," count ",[113,188,137],{"class":123},[113,190,192],{"class":191},"sAOxA"," ref",[113,194,195],{"class":119},"(",[113,197,199],{"class":198},"sIQBb","0",[113,201,202],{"class":119},")\n",[113,204,206,209,211],{"class":115,"line":205},5,[113,207,208],{"class":119},"\u003C\u002F",[113,210,124],{"class":123},[113,212,150],{"class":119},[113,214,216],{"class":115,"line":215},6,[113,217,177],{"emptyLinePlaceholder":176},[113,219,221,223,226],{"class":115,"line":220},7,[113,222,120],{"class":119},[113,224,225],{"class":123},"template",[113,227,150],{"class":119},[113,229,231,234,237,240,242,244,247,249],{"class":115,"line":230},8,[113,232,233],{"class":119},"  \u003C",[113,235,236],{"class":123},"button",[113,238,239],{"class":127}," @click",[113,241,137],{"class":123},[113,243,141],{"class":140},[113,245,246],{"class":144},"count++",[113,248,141],{"class":140},[113,250,150],{"class":119},[113,252,254],{"class":115,"line":253},9,[113,255,256],{"class":119},"    count is {{ count }}\n",[113,258,260,263,265],{"class":115,"line":259},10,[113,261,262],{"class":119},"  \u003C\u002F",[113,264,236],{"class":123},[113,266,150],{"class":119},[113,268,270,272,274],{"class":115,"line":269},11,[113,271,208],{"class":119},[113,273,225],{"class":123},[113,275,150],{"class":119},[16,277,278,279,282],{},"Outside Nuxt, ",[20,280,281],{},"@vitejs\u002Fplugin-vue"," compiles Vapor SFCs (the plugin's changelog records Vapor component support, including template-only ones), so use a recent version.",[16,284,285,286,289,290,293,294,297],{},"Vue also accepts ",[20,287,288],{},"\u003Cscript vapor>"," as shorthand for ",[20,291,292],{},"\u003Cscript setup vapor>",". You can instead put the marker on the template with ",[20,295,296],{},"\u003Ctemplate vapor>"," to compile the whole SFC in Vapor mode.",[54,299,301],{"id":300},"vapor-components-in-an-existing-app","Vapor components in an existing app",[16,303,304,305,308,309,312],{},"In an app created with ",[20,306,307],{},"createApp()",", you need to install ",[20,310,311],{},"vaporInteropPlugin"," to use Vapor components:",[104,314,317],{"className":315,"code":316,"language":145,"meta":109,"style":109},"language-ts shiki shiki-themes dracula","import { createApp, vaporInteropPlugin } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateApp(App).use(vaporInteropPlugin).mount('#app')\n",[20,318,319,334,350,354],{"__ignoreMap":109},[113,320,321,323,326,328,330,332],{"class":115,"line":116},[113,322,156],{"class":123},[113,324,325],{"class":119}," { createApp, vaporInteropPlugin } ",[113,327,162],{"class":123},[113,329,165],{"class":140},[113,331,108],{"class":144},[113,333,170],{"class":140},[113,335,336,338,341,343,345,348],{"class":115,"line":153},[113,337,156],{"class":123},[113,339,340],{"class":119}," App ",[113,342,162],{"class":123},[113,344,165],{"class":140},[113,346,347],{"class":144},".\u002FApp.vue",[113,349,170],{"class":140},[113,351,352],{"class":115,"line":173},[113,353,177],{"emptyLinePlaceholder":176},[113,355,356,359,362,365,368,371,373,376,379,381],{"class":115,"line":180},[113,357,358],{"class":191},"createApp",[113,360,361],{"class":119},"(App).",[113,363,364],{"class":191},"use",[113,366,367],{"class":119},"(vaporInteropPlugin).",[113,369,370],{"class":191},"mount",[113,372,195],{"class":119},[113,374,375],{"class":140},"'",[113,377,378],{"class":144},"#app",[113,380,375],{"class":140},[113,382,202],{"class":119},[16,384,385],{},"With the plugin installed, Vapor and regular components can be nested inside each other. The Vue notes warn that this covers props, events and slots in standard usage, but not every edge case yet. Their advice is to keep distinct regions in the app, each in one mode, and to avoid mixed nesting where you can.",[54,387,389],{"id":388},"a-fully-vapor-app","A fully Vapor app",[16,391,392,393,396],{},"If every component is Vapor, use ",[20,394,395],{},"createVaporApp()",":",[104,398,400],{"className":315,"code":399,"language":145,"meta":109,"style":109},"import { createVaporApp } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateVaporApp(App).mount('#app')\n",[20,401,402,417,431,435],{"__ignoreMap":109},[113,403,404,406,409,411,413,415],{"class":115,"line":116},[113,405,156],{"class":123},[113,407,408],{"class":119}," { createVaporApp } ",[113,410,162],{"class":123},[113,412,165],{"class":140},[113,414,108],{"class":144},[113,416,170],{"class":140},[113,418,419,421,423,425,427,429],{"class":115,"line":153},[113,420,156],{"class":123},[113,422,340],{"class":119},[113,424,162],{"class":123},[113,426,165],{"class":140},[113,428,347],{"class":144},[113,430,170],{"class":140},[113,432,433],{"class":115,"line":173},[113,434,177],{"emptyLinePlaceholder":176},[113,436,437,440,442,444,446,448,450,452],{"class":115,"line":180},[113,438,439],{"class":191},"createVaporApp",[113,441,361],{"class":119},[113,443,370],{"class":191},[113,445,195],{"class":119},[113,447,375],{"class":140},[113,449,378],{"class":144},[113,451,375],{"class":140},[113,453,202],{"class":119},[16,455,456,457,459,460,462],{},"The Virtual DOM runtime then stays out of the bundle. Each component still needs the ",[20,458,101],{}," marker. Components written with render functions or JSX remain Virtual DOM components and need interop. A Vapor app can also install ",[20,461,311],{}," to accept Virtual DOM components, but that pulls the runtime back in and cuts into the bundle savings.",[40,464,466],{"id":465},"vapor-in-nuxt-46","Vapor in Nuxt 4.6",[16,468,469,470,396],{},"Nuxt 4.6, released on October 5, 2026, supports Vapor in interop mode. The app root stays on the Virtual DOM, and you choose which components or pages use Vapor. To enable it, turn the option on in ",[20,471,472],{},"nuxt.config.ts",[104,474,476],{"className":315,"code":475,"language":145,"meta":109,"style":109},"export default defineNuxtConfig({\n  vue: {\n    vapor: true,\n  },\n})\n",[20,477,478,492,502,515,520],{"__ignoreMap":109},[113,479,480,483,486,489],{"class":115,"line":116},[113,481,482],{"class":123},"export",[113,484,485],{"class":123}," default",[113,487,488],{"class":191}," defineNuxtConfig",[113,490,491],{"class":119},"({\n",[113,493,494,497,499],{"class":115,"line":153},[113,495,496],{"class":119},"  vue",[113,498,396],{"class":123},[113,500,501],{"class":119}," {\n",[113,503,504,507,509,512],{"class":115,"line":173},[113,505,506],{"class":119},"    vapor",[113,508,396],{"class":123},[113,510,511],{"class":198}," true",[113,513,514],{"class":119},",\n",[113,516,517],{"class":115,"line":180},[113,518,519],{"class":119},"  },\n",[113,521,522],{"class":115,"line":205},[113,523,524],{"class":119},"})\n",[16,526,527,528,530,531,534,535,538],{},"Then mark components with ",[20,529,292],{},", as in the example above. Nuxt requires Vue ",[20,532,533],{},"^3.6.0-rc.2"," or newer. According to the release post, routing, ",[20,536,537],{},"useAsyncData",", layouts and most built-in components keep working unchanged. The post recommends trying Vapor in a fresh project first.",[16,540,541],{},"The Nuxt docs list a few limitations of interop mode:",[543,544,545,549,556,576,583,602],"ul",{},[546,547,548],"li",{},"the app root can't be Vapor, because fully Vapor apps aren't available in Nuxt;",[546,550,551,552,555],{},"Vapor components don't expose ",[20,553,554],{},"$el"," on template refs;",[546,557,558,561,562,565,566,76,569,83,572,575],{},[20,559,560],{},"\u003CClientOnly>"," skips its attribute forwarding, and ",[20,563,564],{},"\u003CNuxtIsland>",", server components and the head components that read text children (",[20,567,568],{},"\u003CTitle>",[20,570,571],{},"\u003CStyle>",[20,573,574],{},"\u003CNoscript>",") warn if you pass them a Vapor slot;",[546,577,578,579,582],{},"keyed ",[20,580,581],{},"onPrehydrate"," falls back to the unkeyed form (and warns in development), because there is no component instance to attach the key to;",[546,584,585,83,588,591,592,595,596,598,599,601],{},[20,586,587],{},"asyncData",[20,589,590],{},"fetchKey"," through ",[20,593,594],{},"defineNuxtComponent"," don't work (use ",[20,597,537],{}," in ",[20,600,97],{},");",[546,603,604,605,608],{},"Nuxt composables called after an ",[20,606,607],{},"await"," may lose their context.",[16,610,611,612,615,616,619],{},"The same release post has performance numbers for Nuxt 4.6: ",[20,613,614],{},"nuxt build"," on a starter project went from 4.4 s to 3.6 s (18% faster), and a page with 300 ",[20,617,618],{},"\u003CNuxtLink>","s went from 110 to 140 req\u002Fs in SSR (+26%). Nuxt credits those gains to general optimizations in the release, not to Vapor.",[16,621,622],{},"If you're still on Nuxt 3, it reached end of life on July 31, 2026.",[40,624,626],{"id":625},"what-vapor-doesnt-support-yet","What Vapor doesn't support yet",[16,628,629],{},"Vapor supports a subset of Vue's APIs. The official notes list what is unavailable or doesn't apply in Vapor components:",[543,631,632,650,655,664,670,675],{},[546,633,634,635,76,638,76,641,644,645,649],{},"the Options API (",[20,636,637],{},"data",[20,639,640],{},"methods",[20,642,643],{},"mounted"," and so on). If you want a refresher on the difference, see ",[66,646,648],{"href":647},"\u002Fen\u002Fblog\u002Fwhat-is-the-difference-between-composition-api-and-options-api-in-vuejs","Composition API vs Options API",";",[546,651,652,649],{},[20,653,654],{},"app.config.globalProperties",[546,656,657,660,661,649],{},[20,658,659],{},"getCurrentInstance()",", which returns ",[20,662,663],{},"null",[546,665,666,667,649],{},"per-element lifecycle events such as ",[20,668,669],{},"@vue:mounted",[546,671,672,649],{},[20,673,674],{},"v-memo",[546,676,677,76,679,76,682,76,685,83,688,691],{},[20,678,554],{},[20,680,681],{},"$props",[20,683,684],{},"$attrs",[20,686,687],{},"$slots",[20,689,690],{},"$refs"," on component template refs.",[16,693,694,695,697,698,700],{},"Component libraries built on the Virtual DOM also deserve testing. The notes mention \"rough edges\" when using a Virtual DOM component library inside Vapor, without naming any library. If one of them relies on ",[20,696,659],{},", the ",[20,699,663],{}," above could be a problem, but only a test in your own project will tell.",[16,702,703,704,707],{},"The official notes don't list ",[20,705,706],{},"\u003CSuspense>"," or Vue DevTools as incompatible with Vapor, but that doesn't guarantee full support. The RC changelog records several fixes for Vapor and Suspense integration, so support is still being worked on. Test both in your own setup before deciding.",[16,709,710],{},"The same goes for SSR. The RC changelog has several hydration fixes for Vapor components, which suggests SSR is supported but still being tuned. If your app renders on the server, test hydration carefully.",[54,712,714,715],{"id":713},"events-and-delegate","Events and ",[20,716,717],{},".delegate",[16,719,720,721,724,725,728,729,731],{},"In earlier RCs, Vapor delegated events to ",[20,722,723],{},"document",", so a ",[20,726,727],{},"stopPropagation()"," on an ancestor could stop a child's handler. Since rc.2, listeners are attached directly to the element, as in regular Vue, and delegation became opt-in through the ",[20,730,717],{}," modifier:",[104,733,735],{"className":106,"code":734,"language":108,"meta":109,"style":109},"\u003Cbutton @click.delegate=\"onClick\" \u002F>\n",[20,736,737],{"__ignoreMap":109},[113,738,739,741,743,746,749,752,755,757,759,762,764],{"class":115,"line":116},[113,740,120],{"class":119},[113,742,236],{"class":123},[113,744,745],{"class":119}," @",[113,747,748],{"class":127},"click",[113,750,751],{"class":119},".",[113,753,754],{"class":127},"delegate",[113,756,137],{"class":123},[113,758,141],{"class":140},[113,760,761],{"class":119},"onClick",[113,763,141],{"class":140},[113,765,766],{"class":119}," \u002F>\n",[16,768,769,770,773],{},"The ",[20,771,772],{},"compilerOptions.eventDelegation"," option was removed.",[54,775,777],{"id":776},"custom-directives","Custom directives",[16,779,780],{},"Custom directives also have a different interface in Vapor. Instead of an object with hooks, you write a function that receives the element and a reactive getter, and it can return a cleanup function:",[104,782,784],{"className":315,"code":783,"language":145,"meta":109,"style":109},"import { watchEffect } from 'vue'\n\nconst MyDirective = (el, source) => {\n  watchEffect(() => {\n    el.textContent = source()\n  })\n  return () => console.log('cleanup')\n}\n",[20,785,786,801,805,835,847,860,865,892],{"__ignoreMap":109},[113,787,788,790,793,795,797,799],{"class":115,"line":116},[113,789,156],{"class":123},[113,791,792],{"class":119}," { watchEffect } ",[113,794,162],{"class":123},[113,796,165],{"class":140},[113,798,108],{"class":144},[113,800,170],{"class":140},[113,802,803],{"class":115,"line":153},[113,804,177],{"emptyLinePlaceholder":176},[113,806,807,809,812,815,818,822,824,827,830,833],{"class":115,"line":173},[113,808,183],{"class":123},[113,810,811],{"class":191}," MyDirective",[113,813,814],{"class":123}," =",[113,816,817],{"class":119}," (",[113,819,821],{"class":820},"sGEwX","el",[113,823,76],{"class":119},[113,825,826],{"class":820},"source",[113,828,829],{"class":119},") ",[113,831,832],{"class":123},"=>",[113,834,501],{"class":119},[113,836,837,840,843,845],{"class":115,"line":180},[113,838,839],{"class":191},"  watchEffect",[113,841,842],{"class":119},"(() ",[113,844,832],{"class":123},[113,846,501],{"class":119},[113,848,849,852,854,857],{"class":115,"line":205},[113,850,851],{"class":119},"    el.textContent ",[113,853,137],{"class":123},[113,855,856],{"class":191}," source",[113,858,859],{"class":119},"()\n",[113,861,862],{"class":115,"line":215},[113,863,864],{"class":119},"  })\n",[113,866,867,870,873,875,878,881,883,885,888,890],{"class":115,"line":220},[113,868,869],{"class":123},"  return",[113,871,872],{"class":119}," () ",[113,874,832],{"class":123},[113,876,877],{"class":119}," console.",[113,879,880],{"class":191},"log",[113,882,195],{"class":119},[113,884,375],{"class":140},[113,886,887],{"class":144},"cleanup",[113,889,375],{"class":140},[113,891,202],{"class":119},[113,893,894],{"class":115,"line":230},[113,895,896],{"class":119},"}\n",[16,898,899,900,903,904,907,908,751],{},"The full signature is ",[20,901,902],{},"(node, value?, argument?, modifiers?)",", where ",[20,905,906],{},"value"," is the reactive getter. For a refresher on the traditional model, see ",[66,909,911],{"href":910},"\u002Fen\u002Fblog\u002Fwhat-are-directives-in-vue-and-how-to-implement-them","what directives are in Vue",[40,913,915],{"id":914},"where-vapor-helps","Where Vapor helps",[16,917,918],{},"The gain shows up where an app does a lot of DOM update work: dense lists and tables, components that change several times per second, and pages where initial JavaScript size matters. For an ordinary CRUD app, users will likely not notice the difference.",[16,920,921,922,925],{},"Vapor also doesn't fix problems that don't come from the renderer. Rendering 10,000 items in the DOM is slow in both modes, and the fix is to virtualize the list. Calling heavy functions straight from the template, like ",[20,923,924],{},"{{ calculateTotal(item) }}",", is still a bad idea in both modes.",[40,927,929],{"id":928},"should-you-adopt-it-now","Should you adopt it now?",[16,931,932],{},"The Vue team says Vapor's feature set is complete in the RC and recommends two uses for now: applying Vapor to parts of an existing app, such as a performance-sensitive page, and building small new apps entirely in Vapor.",[16,934,935],{},"For a production app that already runs well on Vue 3.5, waiting for the stable release is the safer choice. For an isolated page where you've already measured a bottleneck, a trial in Nuxt 4.6 or in a Vue app with interop is a reasonable experiment. Before you start, check that:",[543,937,938,941,947,950],{},[546,939,940],{},"you measured current performance (the browser's Performance panel works) and found a rendering bottleneck;",[546,942,943,944,946],{},"the component uses ",[20,945,97],{}," or only a template;",[546,948,949],{},"the UI libraries it relies on work with interop in your project;",[546,951,952],{},"you can isolate Vapor to one route or component and back it out without pain.",[40,954,956],{"id":955},"frequently-asked-questions","Frequently asked questions",[54,958,960],{"id":959},"what-is-vapor-mode-in-vue","What is Vapor Mode in Vue?",[16,962,963],{},"It's an opt-in compilation mode for SFCs, available in the Vue 3.6 RC. Instead of building and diffing a Virtual DOM tree at runtime, the compiler generates code that updates the DOM directly.",[54,965,967],{"id":966},"is-vue-36-stable-yet","Is Vue 3.6 stable yet?",[16,969,970,971,973,974,751],{},"Not yet. As of October 6, 2026, the latest version is ",[20,972,22],{}," and the stable release is ",[20,975,26],{},[54,977,979],{"id":978},"does-vapor-mode-replace-the-virtual-dom","Does Vapor Mode replace the Virtual DOM?",[16,981,982,983,751],{},"No. It's optional and lives alongside the Virtual DOM in the same app through ",[20,984,311],{},[54,986,988],{"id":987},"do-i-need-to-rewrite-my-app-to-use-vapor","Do I need to rewrite my app to use Vapor?",[16,990,991,992,994,995,997],{},"No. You can mark one component at a time with ",[20,993,101],{},". Components that use the Options API stay out, because Vapor only accepts ",[20,996,97],{}," and templates.",[54,999,1001],{"id":1000},"does-vapor-mode-work-with-the-options-api","Does Vapor Mode work with the Options API?",[16,1003,1004],{},"No. The official notes list the Options API as unsupported.",[54,1006,1008],{"id":1007},"does-vapor-mode-work-with-nuxt","Does Vapor Mode work with Nuxt?",[16,1010,1011,1012,598,1015,1017,1018,1020],{},"Yes, since Nuxt 4.6, in interop mode: set ",[20,1013,1014],{},"vue: { vapor: true }",[20,1016,472],{}," and mark components with ",[20,1019,292],{},". The app root stays on the Virtual DOM.",[54,1022,1024],{"id":1023},"is-vapor-mode-faster-than-the-virtual-dom","Is Vapor Mode faster than the Virtual DOM?",[16,1026,1027],{},"According to the Vue 3.6 notes, Vapor reached Solid and Svelte 5 levels in third-party benchmarks. For most apps the perceived gain is small, and the real effect on your project only shows up when you measure it.",[1029,1030,1031],"style",{},"html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sY_PY, html code.shiki .sY_PY{--shiki-default:#50FA7B;--shiki-default-font-style:italic}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}",{"title":109,"searchDepth":153,"depth":153,"links":1033},[1034,1037,1041,1042,1047,1048,1049],{"id":42,"depth":153,"text":43,"children":1035},[1036],{"id":56,"depth":173,"text":57},{"id":90,"depth":153,"text":91,"children":1038},[1039,1040],{"id":300,"depth":173,"text":301},{"id":388,"depth":173,"text":389},{"id":465,"depth":153,"text":466},{"id":625,"depth":153,"text":626,"children":1043},[1044,1046],{"id":713,"depth":173,"text":1045},"Events and .delegate",{"id":776,"depth":173,"text":777},{"id":914,"depth":153,"text":915},{"id":928,"depth":153,"text":929},{"id":955,"depth":153,"text":956,"children":1050},[1051,1052,1053,1054,1055,1056,1057],{"id":959,"depth":173,"text":960},{"id":966,"depth":173,"text":967},{"id":978,"depth":173,"text":979},{"id":987,"depth":173,"text":988},{"id":1000,"depth":173,"text":1001},{"id":1007,"depth":173,"text":1008},{"id":1023,"depth":173,"text":1024},[1059,1060],"vuejs","nuxt","What Vue Vapor Mode is, how to turn it on in Vue 3.6 (RC) and Nuxt 4.6, what it doesn't support yet, and when it's worth trying, with code examples and an FAQ.","vue-vapor-mode-what-it-is-and-how-to-use-it","md",{},"\u002Fen\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it","vue-vapor-mode-como-funciona-e-como-usar","2026-10-06",{"title":6,"description":1061},{"loc":1065,"lastmod":1067},"en\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it",1612,"t7xqEIHrQ1NF0PTd4QIeo_dO6I9PgndCWb5gfcF3uhE",{"id":1074,"title":1075,"articleCover":1076,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":1077,"categoriesIds":2505,"description":2507,"enAlternateSlug":2508,"extension":1063,"meta":2509,"navigation":176,"path":2510,"ptBrAlternateSlug":2511,"publishedAt":2512,"seo":2513,"sitemap":2514,"stem":2515,"updatedAt":1067,"wordCount":2516,"__hash__":2517},"blogEn\u002Fen\u002Fblog\u002Fhow-to-validate-cpf-with-javascript.md","How to validate a CPF with JavaScript","\u002Fimages\u002Fen\u002Fblog\u002Fjavascript\u002Fhow-to-validate-cpf-with-javascript.webp",{"type":13,"value":1078,"toc":2482},[1079,1082,1089,1093,1096,1104,1107,1110,1114,1117,1125,1128,1134,1137,1143,1146,1152,1155,1161,1164,1170,1176,1180,1183,1189,1192,1198,1201,1207,1210,1216,1219,1225,1232,1237,1240,1244,1251,1254,1788,1791,1912,1922,1925,1948,1954,1961,1969,1973,1976,2168,2171,2175,2179,2182,2223,2230,2237,2241,2244,2251,2278,2285,2288,2292,2295,2298,2332,2335,2339,2342,2345,2347,2351,2354,2361,2364,2368,2371,2384,2387,2390,2422,2426,2429,2432,2435,2439,2448,2451,2455,2458,2461,2465,2471,2479],[16,1080,1081],{},"The CPF (Cadastro de Pessoas Físicas) is the Brazilian individual taxpayer number, and almost every Brazilian sign-up form asks for it. Validating it on the front-end helps avoid mistakes in sign-up, checkout and login forms. On the back-end, validation is a second barrier against mistyped data and basic fraud.",[16,1083,1084,1085,1088],{},"In this article, you will understand the CPF algorithm and implement a ",[35,1086,1087],{},"CPF validation"," function in plain JavaScript, with no external library.",[40,1090,1092],{"id":1091},"what-the-check-digits-are","What the check digits are",[16,1094,1095],{},"A CPF has 11 digits:",[543,1097,1098,1101],{},[546,1099,1100],{},"The first 9 are the base of the document.",[546,1102,1103],{},"The last 2 are the check digits.",[16,1105,1106],{},"These two digits are calculated from the previous ones. The idea is to catch common typing errors, like swapping or dropping a number.",[16,1108,1109],{},"The math check, however, does not confirm that the CPF exists, belongs to a specific person or is in good standing with the Receita Federal (Brazil's federal revenue service). It only checks whether the digits follow the CPF algorithm.",[54,1111,1113],{"id":1112},"first-check-digit","First check digit",[16,1115,1116],{},"Take the CPF without formatting:",[104,1118,1123],{"className":1119,"code":1121,"language":1122,"meta":109},[1120],"language-text","52998224725\n","text",[20,1124,1121],{"__ignoreMap":109},[16,1126,1127],{},"The first 9 digits are:",[104,1129,1132],{"className":1130,"code":1131,"language":1122,"meta":109},[1120],"5 2 9 9 8 2 2 4 7\n",[20,1133,1131],{"__ignoreMap":109},[16,1135,1136],{},"Multiply each number by the weights from 10 down to 2:",[104,1138,1141],{"className":1139,"code":1140,"language":1122,"meta":109},[1120],"5×10 + 2×9 + 9×8 + 9×7 + 8×6 + 2×5 + 2×4 + 4×3 + 7×2\n",[20,1142,1140],{"__ignoreMap":109},[16,1144,1145],{},"The result is:",[104,1147,1150],{"className":1148,"code":1149,"language":1122,"meta":109},[1120],"50 + 18 + 72 + 63 + 48 + 10 + 8 + 12 + 14 = 295\n",[20,1151,1149],{"__ignoreMap":109},[16,1153,1154],{},"Now calculate the remainder of the division by 11:",[104,1156,1159],{"className":1157,"code":1158,"language":1122,"meta":109},[1120],"295 % 11 = 9\n",[20,1160,1158],{"__ignoreMap":109},[16,1162,1163],{},"Since the remainder is greater than 1, we do:",[104,1165,1168],{"className":1166,"code":1167,"language":1122,"meta":109},[1120],"11 - 9 = 2\n",[20,1169,1167],{"__ignoreMap":109},[16,1171,1172,1173,751],{},"So the first check digit is ",[20,1174,1175],{},"2",[54,1177,1179],{"id":1178},"second-check-digit","Second check digit",[16,1181,1182],{},"Now use the first 10 digits, including the check digit you just calculated:",[104,1184,1187],{"className":1185,"code":1186,"language":1122,"meta":109},[1120],"5 2 9 9 8 2 2 4 7 2\n",[20,1188,1186],{"__ignoreMap":109},[16,1190,1191],{},"Apply the weights from 11 down to 2:",[104,1193,1196],{"className":1194,"code":1195,"language":1122,"meta":109},[1120],"5×11 + 2×10 + 9×9 + 9×8 + 8×7 + 2×6 + 2×5 + 4×4 + 7×3 + 2×2\n",[20,1197,1195],{"__ignoreMap":109},[16,1199,1200],{},"The sum is:",[104,1202,1205],{"className":1203,"code":1204,"language":1122,"meta":109},[1120],"55 + 20 + 81 + 72 + 56 + 12 + 10 + 16 + 21 + 4 = 347\n",[20,1206,1204],{"__ignoreMap":109},[16,1208,1209],{},"The remainder of the division by 11 is:",[104,1211,1214],{"className":1212,"code":1213,"language":1122,"meta":109},[1120],"347 % 11 = 6\n",[20,1215,1213],{"__ignoreMap":109},[16,1217,1218],{},"Since the remainder is greater than 1:",[104,1220,1223],{"className":1221,"code":1222,"language":1122,"meta":109},[1120],"11 - 6 = 5\n",[20,1224,1222],{"__ignoreMap":109},[16,1226,1227,1228,1231],{},"The second check digit is ",[20,1229,1230],{},"5",". So the full CPF is:",[104,1233,1235],{"className":1234,"code":1121,"language":1122,"meta":109},[1120],[20,1236,1121],{"__ignoreMap":109},[16,1238,1239],{},"That is the CPF algorithm: calculate the first digit, add it to the sequence and calculate the second.",[40,1241,1243],{"id":1242},"implementation-in-plain-javascript","Implementation in plain JavaScript",[16,1245,1246,1247,1250],{},"The function needs to handle formatted input, like ",[20,1248,1249],{},"529.982.247-25",", and also a string of digits with no punctuation.",[16,1252,1253],{},"One possible implementation is this:",[104,1255,1259],{"className":1256,"code":1257,"language":1258,"meta":109,"style":109},"language-javascript shiki shiki-themes dracula","function validateCPF(cpf) {\n  \u002F\u002F Make sure the input is treated as text\n  cpf = String(cpf).replace(\u002F\\D\u002Fg, \"\");\n\n  \u002F\u002F A valid CPF must have exactly 11 digits\n  if (cpf.length !== 11) {\n    return false;\n  }\n\n  \u002F\u002F Reject CPFs made of the same digit\n  if (\u002F^(\\d)\\1{10}$\u002F.test(cpf)) {\n    return false;\n  }\n\n  \u002F\u002F Calculate the first check digit\n  let sum = 0;\n\n  for (let i = 0; i \u003C 9; i++) {\n    sum += Number(cpf[i]) * (10 - i);\n  }\n\n  let remainder = sum % 11;\n  const firstDigit = remainder \u003C 2 ? 0 : 11 - remainder;\n\n  \u002F\u002F The calculated first digit must match the one provided\n  if (firstDigit !== Number(cpf[9])) {\n    return false;\n  }\n\n  \u002F\u002F Calculate the second check digit\n  sum = 0;\n\n  for (let i = 0; i \u003C 10; i++) {\n    sum += Number(cpf[i]) * (11 - i);\n  }\n\n  remainder = sum % 11;\n  const secondDigit = remainder \u003C 2 ? 0 : 11 - remainder;\n\n  \u002F\u002F The calculated second digit must also match the one provided\n  return secondDigit === Number(cpf[10]);\n}\n","javascript",[20,1260,1261,1277,1283,1321,1325,1330,1346,1357,1362,1366,1371,1404,1413,1418,1423,1429,1445,1450,1484,1513,1518,1523,1542,1575,1580,1586,1607,1616,1621,1626,1632,1644,1649,1677,1699,1704,1709,1725,1753,1758,1764,1783],{"__ignoreMap":109},[113,1262,1263,1266,1269,1271,1274],{"class":115,"line":116},[113,1264,1265],{"class":123},"function",[113,1267,1268],{"class":191}," validateCPF",[113,1270,195],{"class":119},[113,1272,1273],{"class":820},"cpf",[113,1275,1276],{"class":119},") {\n",[113,1278,1279],{"class":115,"line":153},[113,1280,1282],{"class":1281},"shSDL","  \u002F\u002F Make sure the input is treated as text\n",[113,1284,1285,1288,1290,1293,1296,1299,1301,1305,1308,1310,1313,1315,1318],{"class":115,"line":173},[113,1286,1287],{"class":119},"  cpf ",[113,1289,137],{"class":123},[113,1291,1292],{"class":191}," String",[113,1294,1295],{"class":119},"(cpf).",[113,1297,1298],{"class":191},"replace",[113,1300,195],{"class":119},[113,1302,1304],{"class":1303},"s-doY","\u002F",[113,1306,1307],{"class":198},"\\D",[113,1309,1304],{"class":1303},[113,1311,1312],{"class":123},"g",[113,1314,76],{"class":119},[113,1316,1317],{"class":140},"\"\"",[113,1319,1320],{"class":119},");\n",[113,1322,1323],{"class":115,"line":180},[113,1324,177],{"emptyLinePlaceholder":176},[113,1326,1327],{"class":115,"line":205},[113,1328,1329],{"class":1281},"  \u002F\u002F A valid CPF must have exactly 11 digits\n",[113,1331,1332,1335,1338,1341,1344],{"class":115,"line":215},[113,1333,1334],{"class":123},"  if",[113,1336,1337],{"class":119}," (cpf.length ",[113,1339,1340],{"class":123},"!==",[113,1342,1343],{"class":198}," 11",[113,1345,1276],{"class":119},[113,1347,1348,1351,1354],{"class":115,"line":220},[113,1349,1350],{"class":123},"    return",[113,1352,1353],{"class":198}," false",[113,1355,1356],{"class":119},";\n",[113,1358,1359],{"class":115,"line":230},[113,1360,1361],{"class":119},"  }\n",[113,1363,1364],{"class":115,"line":253},[113,1365,177],{"emptyLinePlaceholder":176},[113,1367,1368],{"class":115,"line":259},[113,1369,1370],{"class":1281},"  \u002F\u002F Reject CPFs made of the same digit\n",[113,1372,1373,1375,1377,1379,1382,1385,1388,1391,1394,1396,1398,1401],{"class":115,"line":269},[113,1374,1334],{"class":123},[113,1376,817],{"class":119},[113,1378,1304],{"class":1303},[113,1380,1381],{"class":123},"^",[113,1383,195],{"class":1384},"smvxG",[113,1386,1387],{"class":198},"\\d",[113,1389,1390],{"class":1384},")",[113,1392,1393],{"class":123},"\\1{10}$",[113,1395,1304],{"class":1303},[113,1397,751],{"class":119},[113,1399,1400],{"class":191},"test",[113,1402,1403],{"class":119},"(cpf)) {\n",[113,1405,1407,1409,1411],{"class":115,"line":1406},12,[113,1408,1350],{"class":123},[113,1410,1353],{"class":198},[113,1412,1356],{"class":119},[113,1414,1416],{"class":115,"line":1415},13,[113,1417,1361],{"class":119},[113,1419,1421],{"class":115,"line":1420},14,[113,1422,177],{"emptyLinePlaceholder":176},[113,1424,1426],{"class":115,"line":1425},15,[113,1427,1428],{"class":1281},"  \u002F\u002F Calculate the first check digit\n",[113,1430,1432,1435,1438,1440,1443],{"class":115,"line":1431},16,[113,1433,1434],{"class":123},"  let",[113,1436,1437],{"class":119}," sum ",[113,1439,137],{"class":123},[113,1441,1442],{"class":198}," 0",[113,1444,1356],{"class":119},[113,1446,1448],{"class":115,"line":1447},17,[113,1449,177],{"emptyLinePlaceholder":176},[113,1451,1453,1456,1458,1461,1464,1466,1468,1471,1473,1476,1479,1482],{"class":115,"line":1452},18,[113,1454,1455],{"class":123},"  for",[113,1457,817],{"class":119},[113,1459,1460],{"class":123},"let",[113,1462,1463],{"class":119}," i ",[113,1465,137],{"class":123},[113,1467,1442],{"class":198},[113,1469,1470],{"class":119},"; i ",[113,1472,120],{"class":123},[113,1474,1475],{"class":198}," 9",[113,1477,1478],{"class":119},"; i",[113,1480,1481],{"class":123},"++",[113,1483,1276],{"class":119},[113,1485,1487,1490,1493,1496,1499,1502,1504,1507,1510],{"class":115,"line":1486},19,[113,1488,1489],{"class":119},"    sum ",[113,1491,1492],{"class":123},"+=",[113,1494,1495],{"class":191}," Number",[113,1497,1498],{"class":119},"(cpf[i]) ",[113,1500,1501],{"class":123},"*",[113,1503,817],{"class":119},[113,1505,1506],{"class":198},"10",[113,1508,1509],{"class":123}," -",[113,1511,1512],{"class":119}," i);\n",[113,1514,1516],{"class":115,"line":1515},20,[113,1517,1361],{"class":119},[113,1519,1521],{"class":115,"line":1520},21,[113,1522,177],{"emptyLinePlaceholder":176},[113,1524,1526,1528,1531,1533,1535,1538,1540],{"class":115,"line":1525},22,[113,1527,1434],{"class":123},[113,1529,1530],{"class":119}," remainder ",[113,1532,137],{"class":123},[113,1534,1437],{"class":119},[113,1536,1537],{"class":123},"%",[113,1539,1343],{"class":198},[113,1541,1356],{"class":119},[113,1543,1545,1548,1551,1553,1555,1557,1560,1563,1565,1568,1570,1572],{"class":115,"line":1544},23,[113,1546,1547],{"class":123},"  const",[113,1549,1550],{"class":119}," firstDigit ",[113,1552,137],{"class":123},[113,1554,1530],{"class":119},[113,1556,120],{"class":123},[113,1558,1559],{"class":198}," 2",[113,1561,1562],{"class":123}," ?",[113,1564,1442],{"class":198},[113,1566,1567],{"class":123}," :",[113,1569,1343],{"class":198},[113,1571,1509],{"class":123},[113,1573,1574],{"class":119}," remainder;\n",[113,1576,1578],{"class":115,"line":1577},24,[113,1579,177],{"emptyLinePlaceholder":176},[113,1581,1583],{"class":115,"line":1582},25,[113,1584,1585],{"class":1281},"  \u002F\u002F The calculated first digit must match the one provided\n",[113,1587,1589,1591,1594,1596,1598,1601,1604],{"class":115,"line":1588},26,[113,1590,1334],{"class":123},[113,1592,1593],{"class":119}," (firstDigit ",[113,1595,1340],{"class":123},[113,1597,1495],{"class":191},[113,1599,1600],{"class":119},"(cpf[",[113,1602,1603],{"class":198},"9",[113,1605,1606],{"class":119},"])) {\n",[113,1608,1610,1612,1614],{"class":115,"line":1609},27,[113,1611,1350],{"class":123},[113,1613,1353],{"class":198},[113,1615,1356],{"class":119},[113,1617,1619],{"class":115,"line":1618},28,[113,1620,1361],{"class":119},[113,1622,1624],{"class":115,"line":1623},29,[113,1625,177],{"emptyLinePlaceholder":176},[113,1627,1629],{"class":115,"line":1628},30,[113,1630,1631],{"class":1281},"  \u002F\u002F Calculate the second check digit\n",[113,1633,1635,1638,1640,1642],{"class":115,"line":1634},31,[113,1636,1637],{"class":119},"  sum ",[113,1639,137],{"class":123},[113,1641,1442],{"class":198},[113,1643,1356],{"class":119},[113,1645,1647],{"class":115,"line":1646},32,[113,1648,177],{"emptyLinePlaceholder":176},[113,1650,1652,1654,1656,1658,1660,1662,1664,1666,1668,1671,1673,1675],{"class":115,"line":1651},33,[113,1653,1455],{"class":123},[113,1655,817],{"class":119},[113,1657,1460],{"class":123},[113,1659,1463],{"class":119},[113,1661,137],{"class":123},[113,1663,1442],{"class":198},[113,1665,1470],{"class":119},[113,1667,120],{"class":123},[113,1669,1670],{"class":198}," 10",[113,1672,1478],{"class":119},[113,1674,1481],{"class":123},[113,1676,1276],{"class":119},[113,1678,1680,1682,1684,1686,1688,1690,1692,1695,1697],{"class":115,"line":1679},34,[113,1681,1489],{"class":119},[113,1683,1492],{"class":123},[113,1685,1495],{"class":191},[113,1687,1498],{"class":119},[113,1689,1501],{"class":123},[113,1691,817],{"class":119},[113,1693,1694],{"class":198},"11",[113,1696,1509],{"class":123},[113,1698,1512],{"class":119},[113,1700,1702],{"class":115,"line":1701},35,[113,1703,1361],{"class":119},[113,1705,1707],{"class":115,"line":1706},36,[113,1708,177],{"emptyLinePlaceholder":176},[113,1710,1712,1715,1717,1719,1721,1723],{"class":115,"line":1711},37,[113,1713,1714],{"class":119},"  remainder ",[113,1716,137],{"class":123},[113,1718,1437],{"class":119},[113,1720,1537],{"class":123},[113,1722,1343],{"class":198},[113,1724,1356],{"class":119},[113,1726,1728,1730,1733,1735,1737,1739,1741,1743,1745,1747,1749,1751],{"class":115,"line":1727},38,[113,1729,1547],{"class":123},[113,1731,1732],{"class":119}," secondDigit ",[113,1734,137],{"class":123},[113,1736,1530],{"class":119},[113,1738,120],{"class":123},[113,1740,1559],{"class":198},[113,1742,1562],{"class":123},[113,1744,1442],{"class":198},[113,1746,1567],{"class":123},[113,1748,1343],{"class":198},[113,1750,1509],{"class":123},[113,1752,1574],{"class":119},[113,1754,1756],{"class":115,"line":1755},39,[113,1757,177],{"emptyLinePlaceholder":176},[113,1759,1761],{"class":115,"line":1760},40,[113,1762,1763],{"class":1281},"  \u002F\u002F The calculated second digit must also match the one provided\n",[113,1765,1767,1769,1771,1774,1776,1778,1780],{"class":115,"line":1766},41,[113,1768,869],{"class":123},[113,1770,1732],{"class":119},[113,1772,1773],{"class":123},"===",[113,1775,1495],{"class":191},[113,1777,1600],{"class":119},[113,1779,1506],{"class":198},[113,1781,1782],{"class":119},"]);\n",[113,1784,1786],{"class":115,"line":1785},42,[113,1787,896],{"class":119},[16,1789,1790],{},"Usage examples:",[104,1792,1794],{"className":1256,"code":1793,"language":1258,"meta":109,"style":109},"console.log(validateCPF(\"529.982.247-25\"));\n\u002F\u002F true\n\nconsole.log(validateCPF(\"52998224725\"));\n\u002F\u002F true\n\nconsole.log(validateCPF(\"529.982.247-26\"));\n\u002F\u002F false\n\nconsole.log(validateCPF(\"111.111.111-11\"));\n\u002F\u002F false\n",[20,1795,1796,1819,1824,1828,1849,1853,1857,1878,1883,1887,1908],{"__ignoreMap":109},[113,1797,1798,1801,1803,1805,1808,1810,1812,1814,1816],{"class":115,"line":116},[113,1799,1800],{"class":119},"console.",[113,1802,880],{"class":191},[113,1804,195],{"class":119},[113,1806,1807],{"class":191},"validateCPF",[113,1809,195],{"class":119},[113,1811,141],{"class":140},[113,1813,1249],{"class":144},[113,1815,141],{"class":140},[113,1817,1818],{"class":119},"));\n",[113,1820,1821],{"class":115,"line":153},[113,1822,1823],{"class":1281},"\u002F\u002F true\n",[113,1825,1826],{"class":115,"line":173},[113,1827,177],{"emptyLinePlaceholder":176},[113,1829,1830,1832,1834,1836,1838,1840,1842,1845,1847],{"class":115,"line":180},[113,1831,1800],{"class":119},[113,1833,880],{"class":191},[113,1835,195],{"class":119},[113,1837,1807],{"class":191},[113,1839,195],{"class":119},[113,1841,141],{"class":140},[113,1843,1844],{"class":144},"52998224725",[113,1846,141],{"class":140},[113,1848,1818],{"class":119},[113,1850,1851],{"class":115,"line":205},[113,1852,1823],{"class":1281},[113,1854,1855],{"class":115,"line":215},[113,1856,177],{"emptyLinePlaceholder":176},[113,1858,1859,1861,1863,1865,1867,1869,1871,1874,1876],{"class":115,"line":220},[113,1860,1800],{"class":119},[113,1862,880],{"class":191},[113,1864,195],{"class":119},[113,1866,1807],{"class":191},[113,1868,195],{"class":119},[113,1870,141],{"class":140},[113,1872,1873],{"class":144},"529.982.247-26",[113,1875,141],{"class":140},[113,1877,1818],{"class":119},[113,1879,1880],{"class":115,"line":230},[113,1881,1882],{"class":1281},"\u002F\u002F false\n",[113,1884,1885],{"class":115,"line":253},[113,1886,177],{"emptyLinePlaceholder":176},[113,1888,1889,1891,1893,1895,1897,1899,1901,1904,1906],{"class":115,"line":259},[113,1890,1800],{"class":119},[113,1892,880],{"class":191},[113,1894,195],{"class":119},[113,1896,1807],{"class":191},[113,1898,195],{"class":119},[113,1900,141],{"class":140},[113,1902,1903],{"class":144},"111.111.111-11",[113,1905,141],{"class":140},[113,1907,1818],{"class":119},[113,1909,1910],{"class":115,"line":269},[113,1911,1882],{"class":1281},[16,1913,769,1914,1917,1918,83,1920,751],{},[20,1915,1916],{},"replace(\u002F\\D\u002Fg, '')"," call removes everything that is not a digit. That lets the function accept both ",[20,1919,1249],{},[20,1921,1844],{},[16,1923,1924],{},"This is the regular expression that catches repeated digits:",[104,1926,1928],{"className":1256,"code":1927,"language":1258,"meta":109,"style":109},"\u002F^(\\d)\\1{10}$\u002F;\n",[20,1929,1930],{"__ignoreMap":109},[113,1931,1932,1934,1936,1938,1940,1942,1944,1946],{"class":115,"line":116},[113,1933,1304],{"class":1303},[113,1935,1381],{"class":123},[113,1937,195],{"class":1384},[113,1939,1387],{"class":198},[113,1941,1390],{"class":1384},[113,1943,1393],{"class":123},[113,1945,1304],{"class":1303},[113,1947,1356],{"class":119},[16,1949,1950,1951,1953],{},"It matches an 11-character sequence made of the same digit. That keeps values like ",[20,1952,1903],{}," from being accepted.",[16,1955,1956,1957,1960],{},"This detail tends to catch people who have already struggled with the problem: CPFs with all digits equal pass the math, but they must be rejected explicitly. If you implement only the calculation, your code will treat ",[20,1958,1959],{},"11111111111"," as valid.",[16,1962,1963,1964,751],{},"If you want to test inputs quickly without building a form, use ",[66,1965,1968],{"href":1966,"rel":1967},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcpf-validator",[70],"our free online CPF validator",[54,1970,1972],{"id":1971},"validation-in-the-form","Validation in the form",[16,1974,1975],{},"You can call the function on the form's submit event:",[104,1977,1979],{"className":1256,"code":1978,"language":1258,"meta":109,"style":109},"const form = document.querySelector(\"#form\");\nconst cpfField = document.querySelector(\"#cpf\");\nconst message = document.querySelector(\"#message\");\n\nform.addEventListener(\"submit\", function (event) {\n  event.preventDefault();\n\n  if (!validateCPF(cpfField.value)) {\n    message.textContent = \"Enter a valid CPF.\";\n    return;\n  }\n\n  message.textContent = \"Valid CPF.\";\n});\n",[20,1980,1981,2007,2031,2055,2059,2087,2098,2102,2116,2133,2139,2143,2147,2163],{"__ignoreMap":109},[113,1982,1983,1985,1988,1990,1993,1996,1998,2000,2003,2005],{"class":115,"line":116},[113,1984,183],{"class":123},[113,1986,1987],{"class":119}," form ",[113,1989,137],{"class":123},[113,1991,1992],{"class":119}," document.",[113,1994,1995],{"class":191},"querySelector",[113,1997,195],{"class":119},[113,1999,141],{"class":140},[113,2001,2002],{"class":144},"#form",[113,2004,141],{"class":140},[113,2006,1320],{"class":119},[113,2008,2009,2011,2014,2016,2018,2020,2022,2024,2027,2029],{"class":115,"line":153},[113,2010,183],{"class":123},[113,2012,2013],{"class":119}," cpfField ",[113,2015,137],{"class":123},[113,2017,1992],{"class":119},[113,2019,1995],{"class":191},[113,2021,195],{"class":119},[113,2023,141],{"class":140},[113,2025,2026],{"class":144},"#cpf",[113,2028,141],{"class":140},[113,2030,1320],{"class":119},[113,2032,2033,2035,2038,2040,2042,2044,2046,2048,2051,2053],{"class":115,"line":173},[113,2034,183],{"class":123},[113,2036,2037],{"class":119}," message ",[113,2039,137],{"class":123},[113,2041,1992],{"class":119},[113,2043,1995],{"class":191},[113,2045,195],{"class":119},[113,2047,141],{"class":140},[113,2049,2050],{"class":144},"#message",[113,2052,141],{"class":140},[113,2054,1320],{"class":119},[113,2056,2057],{"class":115,"line":180},[113,2058,177],{"emptyLinePlaceholder":176},[113,2060,2061,2064,2067,2069,2071,2074,2076,2078,2080,2082,2085],{"class":115,"line":205},[113,2062,2063],{"class":119},"form.",[113,2065,2066],{"class":191},"addEventListener",[113,2068,195],{"class":119},[113,2070,141],{"class":140},[113,2072,2073],{"class":144},"submit",[113,2075,141],{"class":140},[113,2077,76],{"class":119},[113,2079,1265],{"class":123},[113,2081,817],{"class":119},[113,2083,2084],{"class":820},"event",[113,2086,1276],{"class":119},[113,2088,2089,2092,2095],{"class":115,"line":215},[113,2090,2091],{"class":119},"  event.",[113,2093,2094],{"class":191},"preventDefault",[113,2096,2097],{"class":119},"();\n",[113,2099,2100],{"class":115,"line":220},[113,2101,177],{"emptyLinePlaceholder":176},[113,2103,2104,2106,2108,2111,2113],{"class":115,"line":230},[113,2105,1334],{"class":123},[113,2107,817],{"class":119},[113,2109,2110],{"class":123},"!",[113,2112,1807],{"class":191},[113,2114,2115],{"class":119},"(cpfField.value)) {\n",[113,2117,2118,2121,2123,2126,2129,2131],{"class":115,"line":253},[113,2119,2120],{"class":119},"    message.textContent ",[113,2122,137],{"class":123},[113,2124,2125],{"class":140}," \"",[113,2127,2128],{"class":144},"Enter a valid CPF.",[113,2130,141],{"class":140},[113,2132,1356],{"class":119},[113,2134,2135,2137],{"class":115,"line":259},[113,2136,1350],{"class":123},[113,2138,1356],{"class":119},[113,2140,2141],{"class":115,"line":269},[113,2142,1361],{"class":119},[113,2144,2145],{"class":115,"line":1406},[113,2146,177],{"emptyLinePlaceholder":176},[113,2148,2149,2152,2154,2156,2159,2161],{"class":115,"line":1415},[113,2150,2151],{"class":119},"  message.textContent ",[113,2153,137],{"class":123},[113,2155,2125],{"class":140},[113,2157,2158],{"class":144},"Valid CPF.",[113,2160,141],{"class":140},[113,2162,1356],{"class":119},[113,2164,2165],{"class":115,"line":1420},[113,2166,2167],{"class":119},"});\n",[16,2169,2170],{},"This validation improves the experience in the browser, but it does not replace validation on the server. Any code that runs on the front-end can be changed or skipped by the client. The back-end should repeat the validation before saving or processing the data.",[40,2172,2174],{"id":2173},"common-mistakes","Common mistakes",[54,2176,2178],{"id":2177},"validating-a-cpf-with-a-format-regex-only","Validating a CPF with a format regex only",[16,2180,2181],{},"An expression like this one:",[104,2183,2185],{"className":1256,"code":2184,"language":1258,"meta":109,"style":109},"\u002F^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$\u002F;\n",[20,2186,2187],{"__ignoreMap":109},[113,2188,2189,2191,2193,2195,2198,2201,2203,2205,2207,2209,2211,2214,2216,2219,2221],{"class":115,"line":116},[113,2190,1304],{"class":1303},[113,2192,1381],{"class":123},[113,2194,1387],{"class":198},[113,2196,2197],{"class":123},"{3}",[113,2199,2200],{"class":144},"\\.",[113,2202,1387],{"class":198},[113,2204,2197],{"class":123},[113,2206,2200],{"class":144},[113,2208,1387],{"class":198},[113,2210,2197],{"class":123},[113,2212,2213],{"class":144},"-",[113,2215,1387],{"class":198},[113,2217,2218],{"class":123},"{2}$",[113,2220,1304],{"class":1303},[113,2222,1356],{"class":119},[16,2224,2225,2226,2229],{},"only checks whether the text has the ",[20,2227,2228],{},"000.000.000-00"," format. It does not calculate the check digits.",[16,2231,2232,2233,2236],{},"For example, ",[20,2234,2235],{},"123.456.789-00"," can be perfectly formatted and still be an invalid CPF. A regex can help check what the input looks like, but it does not replace the CPF algorithm.",[54,2238,2240],{"id":2239},"forgetting-leading-zeros","Forgetting leading zeros",[16,2242,2243],{},"A CPF is an identifier, not a number for regular math. So treat it as a string.",[16,2245,2246,2247,2250],{},"If you convert a CPF to ",[20,2248,2249],{},"Number",", a value that starts with zero can lose that zero:",[104,2252,2254],{"className":1256,"code":2253,"language":1258,"meta":109,"style":109},"const cpf = Number(\"012.345.678-90\");\n",[20,2255,2256],{"__ignoreMap":109},[113,2257,2258,2260,2263,2265,2267,2269,2271,2274,2276],{"class":115,"line":116},[113,2259,183],{"class":123},[113,2261,2262],{"class":119}," cpf ",[113,2264,137],{"class":123},[113,2266,1495],{"class":191},[113,2268,195],{"class":119},[113,2270,141],{"class":140},[113,2272,2273],{"class":144},"012.345.678-90",[113,2275,141],{"class":140},[113,2277,1320],{"class":119},[16,2279,2280,2281,2284],{},"Also, very large numbers should not be the main representation of documents. The function in this article converts the input to text with ",[20,2282,2283],{},"String(cpf)"," and removes the formatting before calculating.",[16,2286,2287],{},"In practice, keep the value from the HTML field as a string from the start.",[54,2289,2291],{"id":2290},"not-normalizing-the-input","Not normalizing the input",[16,2293,2294],{},"If the code expects exactly 11 digits, it may reject a formatted CPF before it even runs the calculation.",[16,2296,2297],{},"This step fixes the problem:",[104,2299,2301],{"className":1256,"code":2300,"language":1258,"meta":109,"style":109},"cpf = String(cpf).replace(\u002F\\D\u002Fg, \"\");\n",[20,2302,2303],{"__ignoreMap":109},[113,2304,2305,2308,2310,2312,2314,2316,2318,2320,2322,2324,2326,2328,2330],{"class":115,"line":116},[113,2306,2307],{"class":119},"cpf ",[113,2309,137],{"class":123},[113,2311,1292],{"class":191},[113,2313,1295],{"class":119},[113,2315,1298],{"class":191},[113,2317,195],{"class":119},[113,2319,1304],{"class":1303},[113,2321,1307],{"class":198},[113,2323,1304],{"class":1303},[113,2325,1312],{"class":123},[113,2327,76],{"class":119},[113,2329,1317],{"class":140},[113,2331,1320],{"class":119},[16,2333,2334],{},"It removes dots, dashes, spaces and other non-numeric characters. After that, the function checks whether exactly 11 digits are left.",[54,2336,2338],{"id":2337},"validating-only-in-the-browser","Validating only in the browser",[16,2340,2341],{},"Front-end validation is useful for instant feedback, but it is not a security layer. A user can send a request straight to the server without going through the page's JavaScript.",[16,2343,2344],{},"The same rule should exist on the back-end, preferably in a single function covered by tests.",[40,2346,956],{"id":955},[54,2348,2350],{"id":2349},"how-do-i-know-if-a-cpf-is-valid","How do I know if a CPF is valid?",[16,2352,2353],{},"First, remove the dots and dash and confirm that 11 digits are left. Then reject sequences with all digits equal and calculate the two check digits according to the CPF algorithm.",[16,2355,2356,2357,2360],{},"The CPF is only mathematically valid if the two calculated digits match the last two digits provided. The ",[20,2358,2359],{},"validateCPF(cpf)"," function in this article runs all of these steps.",[16,2362,2363],{},"Passing the math check says nothing about whether the document is active, in good standing or tied to a specific person.",[54,2365,2367],{"id":2366},"how-do-i-calculate-the-cpf-check-digit","How do I calculate the CPF check digit?",[16,2369,2370],{},"For the first digit, multiply the 9 base digits by the weights from 10 down to 2. Add up the results and calculate the remainder of the division by 11.",[543,2372,2373,2378],{},[546,2374,2375,2376,751],{},"If the remainder is 0 or 1, the digit is ",[20,2377,199],{},[546,2379,2380,2381,751],{},"Otherwise, the digit is ",[20,2382,2383],{},"11 - remainder",[16,2385,2386],{},"For the second digit, do the same with the first 10 digits and the weights from 11 down to 2.",[16,2388,2389],{},"In JavaScript, the core of the calculation can be written like this:",[104,2391,2393],{"className":1256,"code":2392,"language":1258,"meta":109,"style":109},"const digit = remainder \u003C 2 ? 0 : 11 - remainder;\n",[20,2394,2395],{"__ignoreMap":109},[113,2396,2397,2399,2402,2404,2406,2408,2410,2412,2414,2416,2418,2420],{"class":115,"line":116},[113,2398,183],{"class":123},[113,2400,2401],{"class":119}," digit ",[113,2403,137],{"class":123},[113,2405,1530],{"class":119},[113,2407,120],{"class":123},[113,2409,1559],{"class":198},[113,2411,1562],{"class":123},[113,2413,1442],{"class":198},[113,2415,1567],{"class":123},[113,2417,1343],{"class":198},[113,2419,1509],{"class":123},[113,2421,1574],{"class":119},[54,2423,2425],{"id":2424},"is-validating-a-cpf-the-same-as-checking-it-with-the-receita-federal","Is validating a CPF the same as checking it with the Receita Federal?",[16,2427,2428],{},"No. The math check runs locally: the system calculates the digits and compares the result with the CPF provided.",[16,2430,2431],{},"An official lookup is a different operation. It can check registration details and the status of the CPF in an official database, usually through an authorized service or the channels the Receita Federal provides.",[16,2433,2434],{},"So a CPF can pass the algorithm and still not be in good standing, not exist in the database you checked or not belong to the person who entered it. The JavaScript code does not confirm identity or registration status.",[54,2436,2438],{"id":2437},"can-i-generate-a-valid-cpf-for-testing","Can I generate a valid CPF for testing?",[16,2440,2441,2442,2447],{},"Yes. To test input masks, forms and APIs, you can use numbers that pass the math check without using real data. If you need valid CPF numbers just to test forms, ",[66,2443,2446],{"href":2444,"rel":2445},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcpf-generator",[70],"our CPF generator"," handles that in seconds.",[16,2449,2450],{},"Use test data in a controlled way and make it clear, in your team's environment, that it is fictional. Also avoid putting real documents in public examples, test files or repositories.",[54,2452,2454],{"id":2453},"does-the-function-check-whether-the-cpf-belongs-to-the-user","Does the function check whether the CPF belongs to the user?",[16,2456,2457],{},"No. It only checks the normalized format, the number of digits, the check digit rule and the rejection of repeated sequences.",[16,2459,2460],{},"To confirm ownership, you would need a different step, with an official source or a proper verification flow. Local validation does not replace that process.",[40,2462,2464],{"id":2463},"next-steps","Next steps",[16,2466,2467,2468,2470],{},"You now have the CPF algorithm and a ",[20,2469,2359],{}," function ready to adapt to your project. Put the validation on the front-end to guide the user and repeat the rule on the back-end before storing or accepting the sign-up.",[16,2472,2473,2474,2478],{},"If you want to check values without writing code, use ",[66,2475,2477],{"href":1966,"rel":2476},[70],"our online CPF checker",". After that, it is worth turning the examples in this article into automated tests that cover formatted input, invalid CPFs and documents with leading zeros.",[1029,2480,2481],{},"html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .s-doY, html code.shiki .s-doY{--shiki-default:#FF5555}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .smvxG, html code.shiki .smvxG{--shiki-default:#FFB86C}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}",{"title":109,"searchDepth":153,"depth":153,"links":2483},[2484,2488,2491,2497,2504],{"id":1091,"depth":153,"text":1092,"children":2485},[2486,2487],{"id":1112,"depth":173,"text":1113},{"id":1178,"depth":173,"text":1179},{"id":1242,"depth":153,"text":1243,"children":2489},[2490],{"id":1971,"depth":173,"text":1972},{"id":2173,"depth":153,"text":2174,"children":2492},[2493,2494,2495,2496],{"id":2177,"depth":173,"text":2178},{"id":2239,"depth":173,"text":2240},{"id":2290,"depth":173,"text":2291},{"id":2337,"depth":173,"text":2338},{"id":955,"depth":153,"text":956,"children":2498},[2499,2500,2501,2502,2503],{"id":2349,"depth":173,"text":2350},{"id":2366,"depth":173,"text":2367},{"id":2424,"depth":173,"text":2425},{"id":2437,"depth":173,"text":2438},{"id":2453,"depth":173,"text":2454},{"id":2463,"depth":153,"text":2464},[1258,2506],"tools","Learn how to validate a CPF with plain JavaScript, calculate the check digits, handle formatting and reject invalid sequences in forms, with working code.","how-to-validate-cpf-with-javascript",{},"\u002Fen\u002Fblog\u002Fhow-to-validate-cpf-with-javascript","como-validar-cpf-com-javascript","2026-10-04",{"title":1075,"description":2507},{"loc":2510,"lastmod":1067},"en\u002Fblog\u002Fhow-to-validate-cpf-with-javascript",1253,"rl6GsOytKht8bBLg9yWipV1zFDvkHy7ruCKxlllBid0",{"id":2519,"title":2520,"articleCover":2521,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":2522,"categoriesIds":3264,"description":3265,"enAlternateSlug":3266,"extension":1063,"meta":3267,"navigation":176,"path":3268,"ptBrAlternateSlug":3269,"publishedAt":2512,"seo":3270,"sitemap":3271,"stem":3272,"updatedAt":2512,"wordCount":3273,"__hash__":3274},"blogEn\u002Fen\u002Fblog\u002Fhow-to-validate-credit-card-with-javascript.md","How to validate a credit card with JavaScript (Luhn algorithm)","\u002Fimages\u002Fen\u002Fblog\u002Fjavascript\u002Fhow-to-validate-credit-card-with-javascript.webp",{"type":13,"value":2523,"toc":3241},[2524,2527,2530,2534,2541,2544,2547,2551,2561,2564,2597,2601,2616,2642,2648,2651,2653,2656,3053,3070,3074,3085,3102,3108,3111,3113,3116,3120,3127,3131,3144,3148,3151,3155,3162,3171,3173,3177,3188,3192,3195,3199,3202,3206,3209,3213,3216,3220,3223,3227,3230,3238],[16,2525,2526],{},"You are building a checkout form and need to validate the card field before sending the data to the payment gateway. The most common and recommended approach is to use the Luhn algorithm to check whether the number has the correct mathematical format.",[16,2528,2529],{},"In this article, you will understand exactly how the algorithm works, follow a step-by-step numeric example and implement a JavaScript function ready to use in your project.",[40,2531,2533],{"id":2532},"what-the-luhn-algorithm-is","What the Luhn algorithm is",[16,2535,2536,2537,2540],{},"The Luhn algorithm, also called the \"modulus 10 formula\" or \"mod-10\", was created by Hans Peter Luhn in 1954 and became the industry standard for validating credit card numbers. It calculates a ",[35,2538,2539],{},"check digit"," (the last digit of the number) that catches typing errors, like swapping two digits or typing a wrong number.",[16,2542,2543],{},"The idea is simple: the card issuer (bank or card network) generates the number so that, when you apply the Luhn formula, the result is divisible by 10. If it is not, the number has a formatting error.",[16,2545,2546],{},"This runs on the client side (in the browser) before the data goes to the server. That way you avoid sending invalid requests to the payment gateway and improve the user experience with instant feedback.",[40,2548,2550],{"id":2549},"the-algorithm-step-by-step-with-a-numeric-example","The algorithm step by step with a numeric example",[16,2552,2553,2554,2557,2558,751],{},"Let's validate the Visa test number that many payment platforms use in sandbox: ",[35,2555,2556],{},"4242 4242 4242 4242",". First, we remove spaces and dashes, which leaves ",[20,2559,2560],{},"4242424242424242",[16,2562,2563],{},"Now we apply the Luhn algorithm:",[2565,2566,2567,2573,2579,2585,2591],"ol",{},[546,2568,2569,2572],{},[35,2570,2571],{},"Start from the rightmost digit"," (the last 2) and move left. This first digit is never doubled.",[546,2574,2575,2578],{},[35,2576,2577],{},"Double the value of every second digit"," from there, that is, skip one, double the next, skip one, double the next, and so on.",[546,2580,2581,2584],{},[35,2582,2583],{},"If doubling gives a two-digit result",", add the two digits together (or subtract 9, which is equivalent).",[546,2586,2587,2590],{},[35,2588,2589],{},"Add up all the digits"," (the doubled\u002Fadjusted ones and the ones left unchanged).",[546,2592,2593,2596],{},[35,2594,2595],{},"If the total is divisible by 10",", the number is valid according to the Luhn algorithm.",[54,2598,2600],{"id":2599},"hands-on-example-with-4242424242424242","Hands-on example with 4242424242424242",[16,2602,2603,2604,83,2607,2609,2610,2612,2613,2615],{},"The number has 16 digits, alternating ",[20,2605,2606],{},"4",[20,2608,1175],{},". Starting from the rightmost digit (which is not doubled) and moving left, the digits in the \"double\" position are always the ",[20,2611,2606],{},"s, and the ones in the \"don't double\" position are always the ",[20,2614,1175],{},"s. That happens because the whole number follows this alternating pattern.",[543,2617,2618,2627],{},[546,2619,2620,2623,2624,2626],{},[35,2621,2622],{},"Digits that are not doubled"," (8 digits, all ",[20,2625,1175],{},"): 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 = 16",[546,2628,2629,2623,2632,2634,2635,2637,2638,2641],{},[35,2630,2631],{},"Doubled digits",[20,2633,2606],{},"): each doubled ",[20,2636,2606],{}," becomes ",[20,2639,2640],{},"8"," (4 × 2 = 8, not above 9, no adjustment needed). 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 = 64",[16,2643,2644,2645,751],{},"Adding the two groups: 16 + 64 = ",[35,2646,2647],{},"80",[16,2649,2650],{},"Since 80 is divisible by 10 (remainder 0), this number passes the Luhn check.",[40,2652,1243],{"id":1242},[16,2654,2655],{},"Below is a complete function that removes spaces, dashes and other non-numeric characters, then applies the Luhn algorithm:",[104,2657,2659],{"className":1256,"code":2658,"language":1258,"meta":109,"style":109},"function validateCreditCard(number) {\n  \u002F\u002F Remove everything that is not a digit\n  const digits = number.replace(\u002F\\D\u002Fg, \"\");\n\n  \u002F\u002F Cards have between 13 and 19 digits (most have 16)\n  if (digits.length \u003C 13 || digits.length > 19) {\n    return false;\n  }\n\n  let sum = 0;\n  let shouldDouble = false; \u002F\u002F starts at the last digit (not doubled)\n\n  \u002F\u002F Walk from right to left\n  for (let i = digits.length - 1; i >= 0; i--) {\n    let d = parseInt(digits[i], 10);\n\n    if (shouldDouble) {\n      d *= 2;\n      \u002F\u002F If the result has two digits, add them up (or subtract 9)\n      if (d > 9) {\n        d -= 9;\n      }\n    }\n\n    sum += d;\n    shouldDouble = !shouldDouble; \u002F\u002F alternate for the next digit\n  }\n\n  return sum % 10 === 0;\n}\n\n\u002F\u002F Testing with the Visa test number\nconsole.log(validateCreditCard(\"4242 4242 4242 4242\")); \u002F\u002F true\nconsole.log(validateCreditCard(\"4242424242424242\")); \u002F\u002F true\nconsole.log(validateCreditCard(\"1234 5678 1234 5678\")); \u002F\u002F false\n",[20,2660,2661,2675,2680,2710,2714,2719,2745,2753,2757,2761,2773,2790,2794,2799,2832,2852,2856,2864,2876,2881,2895,2907,2912,2917,2921,2930,2946,2950,2954,2971,2975,2979,2984,3008,3030],{"__ignoreMap":109},[113,2662,2663,2665,2668,2670,2673],{"class":115,"line":116},[113,2664,1265],{"class":123},[113,2666,2667],{"class":191}," validateCreditCard",[113,2669,195],{"class":119},[113,2671,2672],{"class":820},"number",[113,2674,1276],{"class":119},[113,2676,2677],{"class":115,"line":153},[113,2678,2679],{"class":1281},"  \u002F\u002F Remove everything that is not a digit\n",[113,2681,2682,2684,2687,2689,2692,2694,2696,2698,2700,2702,2704,2706,2708],{"class":115,"line":173},[113,2683,1547],{"class":123},[113,2685,2686],{"class":119}," digits ",[113,2688,137],{"class":123},[113,2690,2691],{"class":119}," number.",[113,2693,1298],{"class":191},[113,2695,195],{"class":119},[113,2697,1304],{"class":1303},[113,2699,1307],{"class":198},[113,2701,1304],{"class":1303},[113,2703,1312],{"class":123},[113,2705,76],{"class":119},[113,2707,1317],{"class":140},[113,2709,1320],{"class":119},[113,2711,2712],{"class":115,"line":180},[113,2713,177],{"emptyLinePlaceholder":176},[113,2715,2716],{"class":115,"line":205},[113,2717,2718],{"class":1281},"  \u002F\u002F Cards have between 13 and 19 digits (most have 16)\n",[113,2720,2721,2723,2726,2728,2731,2734,2737,2740,2743],{"class":115,"line":215},[113,2722,1334],{"class":123},[113,2724,2725],{"class":119}," (digits.length ",[113,2727,120],{"class":123},[113,2729,2730],{"class":198}," 13",[113,2732,2733],{"class":123}," ||",[113,2735,2736],{"class":119}," digits.length ",[113,2738,2739],{"class":123},">",[113,2741,2742],{"class":198}," 19",[113,2744,1276],{"class":119},[113,2746,2747,2749,2751],{"class":115,"line":220},[113,2748,1350],{"class":123},[113,2750,1353],{"class":198},[113,2752,1356],{"class":119},[113,2754,2755],{"class":115,"line":230},[113,2756,1361],{"class":119},[113,2758,2759],{"class":115,"line":253},[113,2760,177],{"emptyLinePlaceholder":176},[113,2762,2763,2765,2767,2769,2771],{"class":115,"line":259},[113,2764,1434],{"class":123},[113,2766,1437],{"class":119},[113,2768,137],{"class":123},[113,2770,1442],{"class":198},[113,2772,1356],{"class":119},[113,2774,2775,2777,2780,2782,2784,2787],{"class":115,"line":269},[113,2776,1434],{"class":123},[113,2778,2779],{"class":119}," shouldDouble ",[113,2781,137],{"class":123},[113,2783,1353],{"class":198},[113,2785,2786],{"class":119},"; ",[113,2788,2789],{"class":1281},"\u002F\u002F starts at the last digit (not doubled)\n",[113,2791,2792],{"class":115,"line":1406},[113,2793,177],{"emptyLinePlaceholder":176},[113,2795,2796],{"class":115,"line":1415},[113,2797,2798],{"class":1281},"  \u002F\u002F Walk from right to left\n",[113,2800,2801,2803,2805,2807,2809,2811,2813,2815,2818,2820,2823,2825,2827,2830],{"class":115,"line":1420},[113,2802,1455],{"class":123},[113,2804,817],{"class":119},[113,2806,1460],{"class":123},[113,2808,1463],{"class":119},[113,2810,137],{"class":123},[113,2812,2736],{"class":119},[113,2814,2213],{"class":123},[113,2816,2817],{"class":198}," 1",[113,2819,1470],{"class":119},[113,2821,2822],{"class":123},">=",[113,2824,1442],{"class":198},[113,2826,1478],{"class":119},[113,2828,2829],{"class":123},"--",[113,2831,1276],{"class":119},[113,2833,2834,2837,2840,2842,2845,2848,2850],{"class":115,"line":1425},[113,2835,2836],{"class":123},"    let",[113,2838,2839],{"class":119}," d ",[113,2841,137],{"class":123},[113,2843,2844],{"class":191}," parseInt",[113,2846,2847],{"class":119},"(digits[i], ",[113,2849,1506],{"class":198},[113,2851,1320],{"class":119},[113,2853,2854],{"class":115,"line":1431},[113,2855,177],{"emptyLinePlaceholder":176},[113,2857,2858,2861],{"class":115,"line":1447},[113,2859,2860],{"class":123},"    if",[113,2862,2863],{"class":119}," (shouldDouble) {\n",[113,2865,2866,2869,2872,2874],{"class":115,"line":1452},[113,2867,2868],{"class":119},"      d ",[113,2870,2871],{"class":123},"*=",[113,2873,1559],{"class":198},[113,2875,1356],{"class":119},[113,2877,2878],{"class":115,"line":1486},[113,2879,2880],{"class":1281},"      \u002F\u002F If the result has two digits, add them up (or subtract 9)\n",[113,2882,2883,2886,2889,2891,2893],{"class":115,"line":1515},[113,2884,2885],{"class":123},"      if",[113,2887,2888],{"class":119}," (d ",[113,2890,2739],{"class":123},[113,2892,1475],{"class":198},[113,2894,1276],{"class":119},[113,2896,2897,2900,2903,2905],{"class":115,"line":1520},[113,2898,2899],{"class":119},"        d ",[113,2901,2902],{"class":123},"-=",[113,2904,1475],{"class":198},[113,2906,1356],{"class":119},[113,2908,2909],{"class":115,"line":1525},[113,2910,2911],{"class":119},"      }\n",[113,2913,2914],{"class":115,"line":1544},[113,2915,2916],{"class":119},"    }\n",[113,2918,2919],{"class":115,"line":1577},[113,2920,177],{"emptyLinePlaceholder":176},[113,2922,2923,2925,2927],{"class":115,"line":1582},[113,2924,1489],{"class":119},[113,2926,1492],{"class":123},[113,2928,2929],{"class":119}," d;\n",[113,2931,2932,2935,2937,2940,2943],{"class":115,"line":1588},[113,2933,2934],{"class":119},"    shouldDouble ",[113,2936,137],{"class":123},[113,2938,2939],{"class":123}," !",[113,2941,2942],{"class":119},"shouldDouble; ",[113,2944,2945],{"class":1281},"\u002F\u002F alternate for the next digit\n",[113,2947,2948],{"class":115,"line":1609},[113,2949,1361],{"class":119},[113,2951,2952],{"class":115,"line":1618},[113,2953,177],{"emptyLinePlaceholder":176},[113,2955,2956,2958,2960,2962,2964,2967,2969],{"class":115,"line":1623},[113,2957,869],{"class":123},[113,2959,1437],{"class":119},[113,2961,1537],{"class":123},[113,2963,1670],{"class":198},[113,2965,2966],{"class":123}," ===",[113,2968,1442],{"class":198},[113,2970,1356],{"class":119},[113,2972,2973],{"class":115,"line":1628},[113,2974,896],{"class":119},[113,2976,2977],{"class":115,"line":1634},[113,2978,177],{"emptyLinePlaceholder":176},[113,2980,2981],{"class":115,"line":1646},[113,2982,2983],{"class":1281},"\u002F\u002F Testing with the Visa test number\n",[113,2985,2986,2988,2990,2992,2995,2997,2999,3001,3003,3006],{"class":115,"line":1651},[113,2987,1800],{"class":119},[113,2989,880],{"class":191},[113,2991,195],{"class":119},[113,2993,2994],{"class":191},"validateCreditCard",[113,2996,195],{"class":119},[113,2998,141],{"class":140},[113,3000,2556],{"class":144},[113,3002,141],{"class":140},[113,3004,3005],{"class":119},")); ",[113,3007,1823],{"class":1281},[113,3009,3010,3012,3014,3016,3018,3020,3022,3024,3026,3028],{"class":115,"line":1679},[113,3011,1800],{"class":119},[113,3013,880],{"class":191},[113,3015,195],{"class":119},[113,3017,2994],{"class":191},[113,3019,195],{"class":119},[113,3021,141],{"class":140},[113,3023,2560],{"class":144},[113,3025,141],{"class":140},[113,3027,3005],{"class":119},[113,3029,1823],{"class":1281},[113,3031,3032,3034,3036,3038,3040,3042,3044,3047,3049,3051],{"class":115,"line":1701},[113,3033,1800],{"class":119},[113,3035,880],{"class":191},[113,3037,195],{"class":119},[113,3039,2994],{"class":191},[113,3041,195],{"class":119},[113,3043,141],{"class":140},[113,3045,3046],{"class":144},"1234 5678 1234 5678",[113,3048,141],{"class":140},[113,3050,3005],{"class":119},[113,3052,1882],{"class":1281},[16,3054,3055,3056,3059,3060,3063,3064,3069],{},"This is the function you call in your form's ",[20,3057,3058],{},"onblur"," or ",[20,3061,3062],{},"onsubmit",". If you want to test online before integrating it, we have a ",[66,3065,3068],{"href":3066,"rel":3067},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcredit-card-validator",[70],"free online credit card validator"," that applies exactly this logic.",[40,3071,3073],{"id":3072},"what-this-algorithm-does-not-guarantee","What this algorithm does NOT guarantee",[16,3075,3076,3077,3080,3081,3084],{},"Many people skip this disclaimer and regret it later: ",[35,3078,3079],{},"the Luhn algorithm only validates the FORMAT\u002Fmathematical structure of the number",". It does ",[35,3082,3083],{},"NOT"," check:",[543,3086,3087,3090,3093,3096,3099],{},[546,3088,3089],{},"Whether the card actually exists",[546,3091,3092],{},"Whether it is active or blocked",[546,3094,3095],{},"Whether it has available credit",[546,3097,3098],{},"Whether it belongs to the person filling in the form",[546,3100,3101],{},"Whether it has been reported as stolen or used for fraud",[16,3103,3104,3105,3107],{},"Only the card issuer (bank or card network) can check that, through a real payment gateway (Stripe, Pagar.me, Mercado Pago, etc.). Luhn is just a \"typo filter\". It keeps you from sending ",[20,3106,3046],{}," to the gateway and getting a silly error that could have been caught earlier.",[16,3109,3110],{},"Don't write anything in your code suggesting that passing Luhn means \"a card you can charge\", because that is not true.",[40,3112,2174],{"id":2173},[16,3114,3115],{},"If you have implemented card validation before, you have probably seen (or made) one of these:",[54,3117,3119],{"id":3118},"validating-by-digit-count-only","Validating by digit count only",[16,3121,3122,3123,3126],{},"Many forms only check whether the number has 16 digits. That lets ",[20,3124,3125],{},"1111111111111111"," through, which has 16 digits but fails Luhn. The opposite also happens: American Express cards have 15 digits, so a \"must have 16\" rule breaks the flow for Amex users.",[54,3128,3130],{"id":3129},"not-removing-spacesdashes-before-validating","Not removing spaces\u002Fdashes before validating",[16,3132,3133,3134,3059,3137,3139,3140,3143],{},"The user types ",[20,3135,3136],{},"4242-4242-4242-4242",[20,3138,2556],{},". If you don't clean that up before applying Luhn, validation fails even with the correct number. The line ",[20,3141,3142],{},"number.replace(\u002F\\D\u002Fg, '')"," solves this.",[54,3145,3147],{"id":3146},"treating-luhn-as-a-fraud-check","Treating Luhn as a fraud check",[16,3149,3150],{},"This one is classic: the product team asks to \"check if the card is valid\" and the dev implements Luhn thinking it is a fraud check, which it is not. Fraud prevention requires risk analysis, tokenization, 3D Secure and gateway rules.",[54,3152,3154],{"id":3153},"using-test-numbers-as-if-they-were-real","Using test numbers as if they were real",[16,3156,3157,3158,3161],{},"The test numbers that gateways like Stripe and PayPal provide pass Luhn, but ",[35,3159,3160],{},"they are not real cards",". They only work in a sandbox environment. Never use them in production and never try to \"see if it works\" on a real gateway. That violates the terms and can get your account blocked.",[16,3163,3164,3165,3170],{},"If you need Luhn-valid test data for your forms, our ",[66,3166,3169],{"href":3167,"rel":3168},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcredit-card-generator",[70],"credit card generator for testing"," creates structurally correct (but not real) numbers for you to use in QA.",[40,3172,956],{"id":955},[54,3174,3176],{"id":3175},"how-do-i-know-if-a-card-number-is-valid","How do I know if a card number is valid?",[16,3178,3179,3180,3183,3184,3187],{},"It depends on what you mean by \"valid\". If you mean ",[35,3181,3182],{},"correct format",", use the Luhn algorithm, which is what most gateways expect on the front-end. If you mean ",[35,3185,3186],{},"a card that actually works",", only the payment gateway (through the issuer) can answer that, after you try to charge it.",[54,3189,3191],{"id":3190},"can-i-test-a-payment-form-without-a-real-card","Can I test a payment form without a real card?",[16,3193,3194],{},"Yes. Use the test numbers each gateway provides (Stripe, Pagar.me, Mercado Pago, etc.). They pass Luhn and simulate scenarios like \"payment approved\", \"insufficient funds\", \"3D Secure\", etc. Never use other people's real cards. Use only the sandbox ones.",[54,3196,3198],{"id":3197},"is-validating-a-credit-card-the-same-as-checking-whether-it-is-active","Is validating a credit card the same as checking whether it is active?",[16,3200,3201],{},"No. Validating (with Luhn) checks whether the number has the correct mathematical structure. Checking whether it is active, has credit or is not blocked is the bank's job, done through the gateway when you try to process the payment.",[54,3203,3205],{"id":3204},"what-is-the-luhn-algorithm","What is the Luhn algorithm?",[16,3207,3208],{},"It is a math formula (modulus 10) that calculates a check digit to catch typing errors in numbers like credit cards, phone IMEIs, etc. Hans Peter Luhn created it in 1954 and it became the industry standard.",[54,3210,3212],{"id":3211},"can-i-use-luhn-to-prevent-fraud","Can I use Luhn to prevent fraud?",[16,3214,3215],{},"No. Luhn only catches typos, while fraud prevention requires behavioral analysis, address verification, 3D Secure and the gateway's risk rules.",[54,3217,3219],{"id":3218},"what-is-the-difference-between-luhn-valid-number-and-card-you-can-charge","What is the difference between \"Luhn-valid number\" and \"card you can charge\"?",[16,3221,3222],{},"\"Luhn-valid number\" means correct mathematical format. \"Card you can charge\" means the issuer confirmed the card exists, is active, has credit and is not blocked. Only the gateway, together with the issuer, can answer the second question.",[40,3224,3226],{"id":3225},"wrapping-up","Wrapping up",[16,3228,3229],{},"If you are implementing card validation in a checkout form, the Luhn algorithm is the first step, and the most important one, to avoid silly typing errors. Implement the function above, test it with your gateway's sandbox numbers and always remember: Luhn validates the format, not whether the card actually works.",[16,3231,3232,3233,3237],{},"Want to test your implementation before shipping to production? Try our ",[66,3234,3236],{"href":3066,"rel":3235},[70],"online credit card validator",". It applies exactly the same logic you will use in your code.",[1029,3239,3240],{},"html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sAOxA, html code.shiki .sAOxA{--shiki-default:#50FA7B}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .sGEwX, html code.shiki .sGEwX{--shiki-default:#FFB86C;--shiki-default-font-style:italic}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}html pre.shiki code .s-doY, html code.shiki .s-doY{--shiki-default:#FF5555}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html pre.shiki code .seVfx, html code.shiki .seVfx{--shiki-default:#E9F284}html pre.shiki code .s-mGx, html code.shiki .s-mGx{--shiki-default:#F1FA8C}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":109,"searchDepth":153,"depth":153,"links":3242},[3243,3244,3247,3248,3249,3255,3263],{"id":2532,"depth":153,"text":2533},{"id":2549,"depth":153,"text":2550,"children":3245},[3246],{"id":2599,"depth":173,"text":2600},{"id":1242,"depth":153,"text":1243},{"id":3072,"depth":153,"text":3073},{"id":2173,"depth":153,"text":2174,"children":3250},[3251,3252,3253,3254],{"id":3118,"depth":173,"text":3119},{"id":3129,"depth":173,"text":3130},{"id":3146,"depth":173,"text":3147},{"id":3153,"depth":173,"text":3154},{"id":955,"depth":153,"text":956,"children":3256},[3257,3258,3259,3260,3261,3262],{"id":3175,"depth":173,"text":3176},{"id":3190,"depth":173,"text":3191},{"id":3197,"depth":173,"text":3198},{"id":3204,"depth":173,"text":3205},{"id":3211,"depth":173,"text":3212},{"id":3218,"depth":173,"text":3219},{"id":3225,"depth":153,"text":3226},[1258,2506],"Learn how to validate a credit card number with JavaScript using the Luhn algorithm. A hands-on example, ready-to-use code and what Luhn does not check.","how-to-validate-credit-card-with-javascript",{},"\u002Fen\u002Fblog\u002Fhow-to-validate-credit-card-with-javascript","como-validar-cartao-de-credito-com-javascript",{"title":2520,"description":3265},{"loc":3268,"lastmod":2512},"en\u002Fblog\u002Fhow-to-validate-credit-card-with-javascript",1317,"5gPXkTWBxMl6Rgba4_I126Dm2nIZeJpZMh9Vi5n2Zpc",{"id":3276,"title":3277,"articleCover":3278,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":3279,"categoriesIds":3580,"description":3581,"enAlternateSlug":3582,"extension":1063,"meta":3583,"navigation":176,"path":3584,"ptBrAlternateSlug":3585,"publishedAt":3586,"seo":3587,"sitemap":3588,"stem":3598,"updatedAt":1067,"wordCount":3599,"__hash__":3600},"blogEn\u002Fen\u002Fblog\u002Ffree-developer-tools-hub.md","I Built a Free Web Tools Hub for Developers","\u002Fimages\u002Fen\u002Fblog\u002Ftools\u002Ffree-developer-tools-hub.webp",{"type":13,"value":3280,"toc":3561},[3281,3284,3295,3299,3303,3311,3314,3317,3324,3328,3338,3342,3352,3356,3360,3369,3373,3382,3386,3390,3398,3402,3406,3414,3418,3422,3435,3439,3443,3447,3454,3457,3461,3464,3467,3471,3474,3477,3481,3495,3509,3513,3519,3526,3530,3533,3547,3551],[16,3282,3283],{},"You know that moment when you need to generate a CPF to test a form? Or when you need to validate a fake credit card in your development environment? Throughout my career as a developer, I always relied on different websites for these simple but essential daily tasks.",[16,3285,3286,3287,3294],{},"Each site had different layouts and user experiences, some good, others not so much. Thinking about creating something that centralizes these different tools with a simple interface and excellent user experience, I created the ",[35,3288,3289],{},[66,3290,3293],{"href":3291,"rel":3292},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools",[70],"Código ao Ponto Developer Tools Hub",", a completely free hub that brings together several tools in one place, which are straight to the point and work without the need to create an account.",[40,3296,3298],{"id":3297},"what-tools-are-available-in-the-hub","What tools are available in the Hub?",[54,3300,3302],{"id":3301},"online-planning-poker","Online Planning Poker",[16,3304,3305,3310],{},[66,3306,3309],{"href":3307,"rel":3308},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fplanning-poker",[70],"The most complete tool in the hub",". Create a room, share the link with your team (no registration required), and everyone can vote simultaneously on task estimates.",[16,3312,3313],{},"The tool works in real-time with WebSocket - when someone votes, everyone sees it instantly. It has a session timer, participant list with status, and voting options beyond the traditional Fibonacci sequence.",[16,3315,3316],{},"It's the type of tool that comes in handy when your team needs to do Sprint Planning and can't find a quality, free Planning Poker.",[3318,3319],"img",{"alt":3320,"height":3321,"src":3322,"width":3323},"Planning Poker tool interface",405,"\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-planning-poker-gratuito.webp",768,[54,3325,3327],{"id":3326},"cpf-generator-and-validator","CPF Generator and Validator",[16,3329,3330,3331,3337],{},"One of the most popular tools in the hub. The ",[35,3332,3333],{},[66,3334,3336],{"href":2444,"rel":3335},[70],"CPF Generator"," creates valid numbers instantly for use in testing environments. This access success is due to its good position in Google search result pages for the term \"CPF Generator\", ranging between second and fourth position.",[3318,3339],{"alt":3340,"height":3321,"src":3341,"width":3323},"CPF Generator tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-gerador-de-cpf.webp",[16,3343,3344,3345,3351],{},"As a complement, the ",[35,3346,3347],{},[66,3348,3350],{"href":1966,"rel":3349},[70],"CPF Validator"," was also created, which verifies if a number is structurally valid with immediate feedback.",[3318,3353],{"alt":3354,"height":3321,"src":3355,"width":3323},"CPF Validator tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-validador-de-cpf.webp",[54,3357,3359],{"id":3358},"credit-card-generator-and-validator","Credit Card Generator and Validator",[16,3361,769,3362,3368],{},[35,3363,3364],{},[66,3365,3367],{"href":3167,"rel":3366},[70],"Credit Card Generator"," allows you to select the brand (Visa, Mastercard, American Express, etc.) and creates complete fictitious data: card number, CVV, and expiration date. Each field has a quick copy button next to it.",[3318,3370],{"alt":3371,"height":3321,"src":3372,"width":3323},"Credit Card Generator tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-gerador-de-cartao-de-credito.webp",[16,3374,769,3375,3381],{},[35,3376,3377],{},[66,3378,3380],{"href":3066,"rel":3379},[70],"Credit Card Validator"," automatically identifies the brand and verifies if the number is structurally valid. Essential for testing payment flows.",[3318,3383],{"alt":3384,"height":3321,"src":3385,"width":3323},"Credit Card Validator tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-validador-de-cartao-de-credito.webp",[54,3387,3389],{"id":3388},"character-and-word-counter","Character and Word Counter",[16,3391,3392,3397],{},[66,3393,3396],{"href":3394,"rel":3395},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcharacter-and-word-counter",[70],"Counts in real-time as you type or paste text",". Shows total characters, characters without spaces, number of spaces, and total words. Useful for respecting character limits in APIs or validating form fields.",[3318,3399],{"alt":3400,"height":3321,"src":3401,"width":3323},"Character and Word Counter tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-contador-de-caracteres-e-palavras.webp",[54,3403,3405],{"id":3404},"date-format-converter","Date Format Converter",[16,3407,3408,3413],{},[66,3409,3412],{"href":3410,"rel":3411},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fdate-format-converter",[70],"Converts dates to the most commonly used formats in development",": ISO String, Timestamp, and UTC String. You can use the current date\u002Ftime or enter a specific date, with a quick copy button in the result.",[3318,3415],{"alt":3416,"height":3321,"src":3417,"width":3323},"Date Format Converter tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramentas-gerador-e-conversor-de-formato-de-datas.webp",[54,3419,3421],{"id":3420},"whatsapp-link-generator","WhatsApp Link Generator",[16,3423,3424,3425,3430,3431,3434],{},"Enter the phone number (with country code selection) and the desired message. ",[66,3426,3429],{"href":3427,"rel":3428},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fwhatsapp-message-url-generator",[70],"The tool"," generates a direct link to open the conversation on WhatsApp, with ",[35,3432,3433],{},"real-time preview"," showing exactly how the message will appear, including formatting and emojis. Very useful for testing texts that will be used in those tasks where the user clicks \"Contact via WhatsApp\".",[3318,3436],{"alt":3437,"height":3321,"src":3438,"width":3323},"WhatsApp URL Generator tool interface with pre-filled text","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-gerador-de-link-compartilhavel-no-whatsapp.webp",[40,3440,3442],{"id":3441},"design-and-usability-decisions","Design and Usability Decisions",[54,3444,3446],{"id":3445},"frictionless-no-barriers","Frictionless, No Barriers",[16,3448,3449,3450,3453],{},"The most important project decision was: ",[35,3451,3452],{},"no tool requires registration",". This was non-negotiable from the start. Access, use, done.",[16,3455,3456],{},"For Planning Poker, which is collaborative, you can create rooms with shareable links. No login, no password, no hassle.",[54,3458,3460],{"id":3459},"clean-and-intuitive-interface","Clean and Intuitive Interface",[16,3462,3463],{},"Each tool follows a consistent visual pattern: input area on the left, result on the right, and clearly visible action buttons.",[16,3465,3466],{},"Below each tool, there's an educational section explaining use cases, practical examples, and how that tool can help in daily work. The idea is that even someone who has never used it can quickly understand its value.",[54,3468,3470],{"id":3469},"accessible-in-portuguese-and-english","Accessible in Portuguese and English",[16,3472,3473],{},"From the beginning, I implemented internationalization. The system automatically detects the browser language, but you can manually switch whenever you want.",[16,3475,3476],{},"Portuguese to serve the Brazilian community, English to reach developers worldwide. The architecture is prepared to easily add new languages.",[54,3478,3480],{"id":3479},"responsive-and-fast","Responsive and Fast",[16,3482,3483,3484,3488,3489,3494],{},"All these tools have their ",[66,3485,3487],{"href":3291,"rel":3486},[70],"English versions with I18N"," and are tested with ",[66,3490,3493],{"href":3491,"rel":3492},"https:\u002F\u002Fpagespeed.web.dev\u002F",[70],"Lighthouse\u002FPageSpeed Insights"," to ensure good loading performance and visual stability.",[16,3496,3497,3498,83,3503,3508],{},"The minimum score target was 90 points on PageSpeed Insights across all categories, which is a big challenge, especially when using third-party scripts like ",[66,3499,3502],{"href":3500,"rel":3501},"https:\u002F\u002Fdevelopers.google.com\u002Fanalytics",[70],"Google Analytics",[66,3504,3507],{"href":3505,"rel":3506},"https:\u002F\u002Fadsense.google.com\u002Fstart",[70],"Google Adsense",", but with proper optimizations, they deliver excellent loading speed.",[40,3510,3512],{"id":3511},"the-stack-behind-it","The Stack Behind It",[16,3514,3515,3518],{},[35,3516,3517],{},"Nuxt 4"," was chosen for the frontend for its focus on performance and SEO, very important aspects for good Google ranking.",[16,3520,3521,3522,3525],{},"For the backend, ",[35,3523,3524],{},"NestJS"," was chosen for its modular architecture and native WebSocket support, necessary for Planning Poker. It's also JavaScript-based, facilitating Full Stack development of the project.",[40,3527,3529],{"id":3528},"whats-coming-next","What's Coming Next",[16,3531,3532],{},"The backlog has some interesting ideas:",[543,3534,3535,3541],{},[546,3536,3537,3540],{},[35,3538,3539],{},"Sprint Retro"," for retrospective ceremonies",[546,3542,3543,3546],{},[35,3544,3545],{},"More utilities"," as the community suggests",[40,3548,3550],{"id":3549},"want-to-check-it-out","Want to Check It Out?",[16,3552,3553,3554,3560],{},"The tool has been helping hundreds of users daily, you can also be one of them ",[35,3555,3556],{},[66,3557,3559],{"href":3291,"rel":3558},[70],"by clicking here",", I hope to help you, thank you very much 🙋🏻",{"title":109,"searchDepth":153,"depth":153,"links":3562},[3563,3571,3577,3578,3579],{"id":3297,"depth":153,"text":3298,"children":3564},[3565,3566,3567,3568,3569,3570],{"id":3301,"depth":173,"text":3302},{"id":3326,"depth":173,"text":3327},{"id":3358,"depth":173,"text":3359},{"id":3388,"depth":173,"text":3389},{"id":3404,"depth":173,"text":3405},{"id":3420,"depth":173,"text":3421},{"id":3441,"depth":153,"text":3442,"children":3572},[3573,3574,3575,3576],{"id":3445,"depth":173,"text":3446},{"id":3459,"depth":173,"text":3460},{"id":3469,"depth":173,"text":3470},{"id":3479,"depth":173,"text":3480},{"id":3511,"depth":153,"text":3512},{"id":3528,"depth":153,"text":3529},{"id":3549,"depth":153,"text":3550},[2506],"I created a free web tools hub for developers: CPF generator, validators, Planning Poker and more daily utilities, in Portuguese and English, with no sign-up.","free-developer-tools-hub",{},"\u002Fen\u002Fblog\u002Ffree-developer-tools-hub","hub-de-ferramentas-gratuitas-para-desenvolvedores","2026-01-06",{"title":3277,"description":3581},{"loc":3584,"images":3589,"lastmod":1067},[3590,3591,3592,3593,3594,3595,3596,3597],{"loc":3322},{"loc":3341},{"loc":3355},{"loc":3372},{"loc":3385},{"loc":3401},{"loc":3417},{"loc":3438},"en\u002Fblog\u002Ffree-developer-tools-hub",952,"TaF5lI57uyi5DI04hKg3rGxUwsitM4_NctybZAcquMI",{"id":3602,"title":3603,"articleCover":3604,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":3605,"categoriesIds":3776,"description":3778,"enAlternateSlug":3779,"extension":1063,"meta":3780,"navigation":176,"path":3781,"ptBrAlternateSlug":3782,"publishedAt":3783,"seo":3784,"sitemap":3785,"stem":3786,"updatedAt":3783,"wordCount":3787,"__hash__":3788},"blogEn\u002Fen\u002Fblog\u002Fhow-to-deal-with-envy-from-other-programmers.md","How to Deal with Envy from Other Programmers in Your Career","\u002Fimages\u002Fen\u002Fblog\u002Fcareer\u002Fa-nail-that-stands-out-gets-hammered.webp",{"type":13,"value":3606,"toc":3766},[3607,3611,3618,3627,3631,3638,3641,3645,3652,3663,3669,3673,3679,3682,3686,3693,3697,3740,3747,3751,3754,3760],[40,3608,3610],{"id":3609},"a-nail-that-stands-out-gets-hammered-a-harsh-truth-in-programming-careers","A Nail That Stands Out Gets Hammered: A Harsh Truth in Programming Careers",[16,3612,3613,3614,3617],{},"“",[35,3615,3616],{},"A nail that stands out gets hammered",".” If you already have experience in your career, you've probably noticed this. If you're just starting out, this is a truth that time will teach you, but I'm here to tell you before it happens so you can be prepared.",[16,3619,3620,3621,3623,3624,751],{},"In this article, we'll discuss the challenges that arise throughout a programming career, and why they are not just in the ",[35,3622,20],{},", but mostly in ",[35,3625,3626],{},"people",[40,3628,3630],{"id":3629},"early-career-focus-on-hard-skills","Early Career: Focus on Hard Skills",[16,3632,3633,3634,3637],{},"When you're starting out, most of your time is dedicated to ",[35,3635,3636],{},"hard skills",". This includes learning programming languages, frameworks, libraries, and tools that allow you to deliver quality projects.",[16,3639,3640],{},"This phase is natural and necessary because it guarantees your first opportunity as a developer. This is where you build the technical foundation that will support your career.",[40,3642,3644],{"id":3643},"evolution-soft-skills-become-essential","Evolution: Soft Skills Become Essential",[16,3646,3647,3648,3651],{},"As you grow, you realize that the biggest challenge is no longer technical. ",[35,3649,3650],{},"Soft skills"," become fundamental:",[543,3653,3654,3657,3660],{},[546,3655,3656],{},"Communicating clearly with colleagues and managers",[546,3658,3659],{},"Aligning expectations and coordinating how tasks will be carried out",[546,3661,3662],{},"Ensuring things actually work day-to-day",[16,3664,3665,3666,751],{},"Often, the main obstacle is not the code itself, but the ability to deal with people and complex situations. This is where you learn that ",[35,3667,3668],{},"delivering results involves more than writing lines of code",[40,3670,3672],{"id":3671},"when-you-start-to-stand-out","When You Start to Stand Out",[16,3674,3675,3676,751],{},"If you dedicate yourself and consistently deliver results, you will inevitably start to stand out. This is when unexpected problems may appear: ",[35,3677,3678],{},"envy from colleagues",[16,3680,3681],{},"Programming involves people much more than it involves code. People have feelings. One of them is envy. Fortunately, the number of professionals who behave this way is small, but it's important to be aware of this and be prepared to deal with it when it happens.",[40,3683,3685],{"id":3684},"being-prepared-makes-all-the-difference","Being Prepared Makes All the Difference",[16,3687,3688,3689,3692],{},"Knowing that envy and unfair criticism can appear in the workplace means you ",[35,3690,3691],{},"won't be caught off guard",". Being prepared means more than just recognizing these situations exist — it's acting strategically to stay focused on your growth and protect your reputation.",[54,3694,3696],{"id":3695},"how-to-prepare-in-practice","How to Prepare in Practice",[2565,3698,3699,3708,3716,3724,3732],{},[546,3700,3701,3704,3707],{},[35,3702,3703],{},"Maintain consistency in your work",[3705,3706],"br",{},"\nDeliver results regularly and keep them documented. Doing this makes it harder for colleagues to diminish or take credit for your work.",[546,3709,3710,3713,3715],{},[35,3711,3712],{},"Constantly develop your soft skills",[3705,3714],{},"\nKnowing how to communicate, align expectations, and handle conflicts maturely makes a huge difference. Soft skills are not just “nice to have” — they are essential to navigate the human complexities of the workplace.",[546,3717,3718,3721,3723],{},[35,3719,3720],{},"Avoid reacting emotionally",[3705,3722],{},"\nEnvy can come disguised as criticism or passive-aggressive behavior. Staying calm and responding professionally helps prevent unnecessary conflicts.",[546,3725,3726,3729,3731],{},[35,3727,3728],{},"Focus on your growth",[3705,3730],{},"\nDon't compare yourself to others in a harmful way. Focus on what you can control: your technical development, your posture, and your learning. The more you grow consistently, the stronger you become in the face of adversity.",[546,3733,3734,3737,3739],{},[35,3735,3736],{},"Learn to read the signs",[3705,3738],{},"\nIdentifying behaviors like veiled criticism or attempts to take credit allows you to anticipate and act intelligently instead of reacting impulsively.",[16,3741,3742,3743,3746],{},"Being prepared ultimately means ",[35,3744,3745],{},"not letting envy or negative behavior from colleagues interfere with your development",". It's about turning potentially negative situations into opportunities for learning and strengthening your career.",[40,3748,3750],{"id":3749},"conclusion","Conclusion",[16,3752,3753],{},"Growing as a programmer goes far beyond learning languages or frameworks. It's about standing out, taking responsibility, and dealing with people, feelings, and challenges that don't appear in code.",[16,3755,769,3756,3759],{},[35,3757,3758],{},"nail that stands out may get hammered",", but if you are ready, no one will be able to bend you off your path. Preparation, focus, and consistency are the keys to a solid and successful career.",[3761,3762,3763],"blockquote",{},[16,3764,3765],{},"Remember: envy exists, but it doesn't have to be an obstacle if you know how to maintain your growth and reputation intact.",{"title":109,"searchDepth":153,"depth":153,"links":3767},[3768,3769,3770,3771,3772,3775],{"id":3609,"depth":153,"text":3610},{"id":3629,"depth":153,"text":3630},{"id":3643,"depth":153,"text":3644},{"id":3671,"depth":153,"text":3672},{"id":3684,"depth":153,"text":3685,"children":3773},[3774],{"id":3695,"depth":173,"text":3696},{"id":3749,"depth":153,"text":3750},[3777],"career","Understand how envy can appear in the workplace among programmers and how to be prepared to deal with it while keeping your focus and professional growth.","how-to-deal-with-envy-from-other-programmers",{},"\u002Fen\u002Fblog\u002Fhow-to-deal-with-envy-from-other-programmers","como-lidar-com-inveja-de-outros-programadores","2025-08-29",{"title":3603,"description":3778},{"loc":3781,"lastmod":3783},"en\u002Fblog\u002Fhow-to-deal-with-envy-from-other-programmers",647,"XgxGriEOg5rMOa22pktAHRc1MEAXtdQ_73q7q2QIxjc",{"id":3790,"title":3791,"articleCover":3792,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":8,"articleCoverHeight":9,"articleCoverWidth":10,"authorId":11,"body":3793,"categoriesIds":3950,"description":3951,"enAlternateSlug":3952,"extension":1063,"meta":3953,"navigation":176,"path":3954,"ptBrAlternateSlug":3955,"publishedAt":3956,"seo":3957,"sitemap":3958,"stem":3959,"updatedAt":1067,"wordCount":3960,"__hash__":3961},"blogEn\u002Fen\u002Fblog\u002Fvercel-buys-nuxt-labs.md","Vercel Acquired Nuxt Labs! What Does This Mean for Nuxt?","\u002Fimages\u002Fen\u002Fblog\u002Fnuxt\u002Fvercel-buys-nuxt-labs.webp",{"type":13,"value":3794,"toc":3943},[3795,3806,3809,3813,3822,3825,3839,3842,3846,3853,3859,3867,3872,3876,3883,3903,3906,3910,3913,3919,3924,3926,3933,3940],[16,3796,3797,3798,3801,3802,3805],{},"Last week, ",[35,3799,3800],{},"Vercel","—the company known for developing Next.js—announced the acquisition of ",[35,3803,3804],{},"Nuxt Labs",", a startup created by the core maintainers of the Nuxt.js framework. The news took the community by surprise, raising questions about Nuxt's future and the impact of this move on Vue ecosystem developers.",[16,3807,3808],{},"In this article, let's break down what was acquired, why it happened, and what changes in practice.",[40,3810,3812],{"id":3811},"what-is-nuxt-labs","What is Nuxt Labs?",[16,3814,3815,3817,3818,3821],{},[35,3816,3804],{}," is a company founded by ",[35,3819,3820],{},"Sébastien Chopin"," and part of the Nuxt core team to support the sustainable development of the framework. Since Nuxt is open source and free to use, Nuxt Labs was created to offer paid products that help fund core development.",[16,3823,3824],{},"Some of Nuxt Labs' main products include:",[543,3826,3827,3833],{},[546,3828,3829,3832],{},[35,3830,3831],{},"Nuxt Studio"," — A visual interface for creating content with Nuxt Content",[546,3834,3835,3838],{},[35,3836,3837],{},"Nuxt UI Pro"," — A premium component library built with Tailwind CSS",[16,3840,3841],{},"These tools allowed the core team to work more consistently on Nuxt, supporting major releases like Nuxt 3 and new official modules.",[40,3843,3845],{"id":3844},"why-did-vercel-acquire-nuxt-labs","Why did Vercel acquire Nuxt Labs?",[16,3847,3848,3849,3852],{},"According to the official announcement on Vercel's blog and Nuxt Labs' statement, the motivation is to ",[35,3850,3851],{},"invest in the Vue ecosystem"," and ensure that Nuxt has the support it needs to continue evolving.",[16,3854,3855,3856,751],{},"This move is also part of Vercel's broader strategy to expand its support for multiple meta-frameworks—just like it has done with Next.js, SvelteKit, and Astro. The goal is to make ",[35,3857,3858],{},"Vercel the best platform to host any modern frontend project",[16,3860,3861,3862,396],{},"In their words from the ",[66,3863,3866],{"href":3864,"rel":3865},"https:\u002F\u002Fvercel.com\u002Fblog\u002Fnuxtlabs-joins-vercel",[70],"official announcement",[3761,3868,3869],{},[16,3870,3871],{},"“We want to ensure Nuxt continues to innovate and remains a powerful option for Vue developers. We're excited to welcome Sébastien Chopin and part of the Nuxt Labs team to Vercel.”",[40,3873,3875],{"id":3874},"what-changes-in-practice","What changes in practice?",[16,3877,3878,3879,3882],{},"For now, ",[35,3880,3881],{},"Nuxt will remain open source and independent",". The framework itself was not bought—rather, the company that maintains and builds products around it was. Still, the impact is meaningful:",[543,3884,3885,3891,3897],{},[546,3886,3887,3890],{},[35,3888,3889],{},"More resources"," for Nuxt's ongoing development",[546,3892,3893,3896],{},[35,3894,3895],{},"Core team now employed by Vercel",", gaining more financial stability",[546,3898,3899,3902],{},[35,3900,3901],{},"Tighter integration with the Vercel platform"," (while still supporting alternatives like Netlify or self-hosting)",[16,3904,3905],{},"Paid products such as Nuxt Studio and Nuxt UI Pro are expected to remain available, potentially with improvements backed by Vercel's investment.",[40,3907,3909],{"id":3908},"what-about-the-community","What about the community?",[16,3911,3912],{},"Sébastien Chopin emphasized that the community will continue to play a central role in Nuxt's future. Project governance remains open, and development will continue to happen in public on GitHub.",[16,3914,3915,3916,751],{},"There's also a strong commitment to Vue's ecosystem — Vercel even mentioned plans to ",[35,3917,3918],{},"invest more in frameworks beyond React",[3761,3920,3921],{},[16,3922,3923],{},"“Vue is one of the most used frameworks on Vercel. We want to support all modern frameworks, and Nuxt is a key part of that.”",[40,3925,3750],{"id":3749},[16,3927,3928,3929,3932],{},"It's important to note that ",[35,3930,3931],{},"the Nuxt project itself wasn't bought","—just its main supporting company. With the acquisition of Nuxt Labs, Vercel is showing a real willingness to invest in a broader range of frameworks and not limit itself to React.",[16,3934,3935,3936,3939],{},"In the short term, the expectation is ",[35,3937,3938],{},"more investment, more stability, and faster evolution for Nuxt","—great news for developers working with Vue every day.",[16,3941,3942],{},"Let's keep an eye on what's next in this exciting new chapter.",{"title":109,"searchDepth":153,"depth":153,"links":3944},[3945,3946,3947,3948,3949],{"id":3811,"depth":153,"text":3812},{"id":3844,"depth":153,"text":3845},{"id":3874,"depth":153,"text":3875},{"id":3908,"depth":153,"text":3909},{"id":3749,"depth":153,"text":3750},[1060],"Vercel has announced the acquisition of Nuxt Labs, the company behind the Nuxt ecosystem. Find out what this means for the framework and the community.","vercel-buys-nuxt-labs",{},"\u002Fen\u002Fblog\u002Fvercel-buys-nuxt-labs","vercel-compra-nuxt-labs","2025-07-17",{"title":3791,"description":3951},{"loc":3954,"lastmod":1067},"en\u002Fblog\u002Fvercel-buys-nuxt-labs",540,"ocw7cKaZP5YMNC433fS1UZuinvM1rzeIW2dEe9zAoUQ",1791682338844]