[{"data":1,"prerenderedAt":4367},["ShallowReactive",2],{"en-home-articles":3},[4,482,803,1843,3285,4041],{"id":5,"title":6,"articleCover":7,"articleCoverEmbed":8,"articleCoverEmbedUploadDate":9,"articleCoverHeight":10,"articleCoverWidth":11,"authorId":12,"body":13,"categoriesIds":466,"description":469,"enAlternateSlug":470,"extension":471,"meta":472,"navigation":473,"path":474,"ptBrAlternateSlug":475,"publishedAt":476,"seo":477,"sitemap":478,"stem":479,"updatedAt":476,"wordCount":480,"__hash__":481},"blogEn\u002Fen\u002Fblog\u002Fwhy-is-javascript-called-javascript.md","Why Is JavaScript Called JavaScript? The Story Behind the Name","\u002Fimages\u002Fen\u002Fblog\u002Fhistoria-da-programacao\u002Fwhy-is-javascript-called-javascript.webp","https:\u002F\u002Fwww.youtube.com\u002Fembed\u002FU1balPnfm_8","2026-11-05T12:45:30-03:00",720,1280,"thiago-nunes-batista",{"type":14,"value":15,"toc":453},"minimark",[16,20,23,28,40,49,53,67,70,83,86,116,128,132,139,146,150,161,215,226,250,253,257,264,277,280,284,293,308,311,339,351,355,358,367,371,449],[17,18,19],"p",{},"JavaScript is not Java, it is not a version of Java, and it was not made by the company behind Java. Still, almost every beginner trips over the same question: if the two languages are unrelated, why are the names so close?",[17,21,22],{},"The answer involves a fight between browsers, a prototype written in a rush in 1995, and a trademark that is still caught up in a legal case more than 30 years later. If you'd rather watch, the video above tells this story in four minutes. It's in Portuguese, with captions. This article goes deeper, with dates, comparisons and sources.",[24,25,27],"h2",{"id":26},"why-did-netscape-need-a-language-for-the-browser","Why did Netscape need a language for the browser?",[17,29,30,31,39],{},"In 1995, Netscape Navigator was the most used browser on the internet, and Microsoft was arriving with Internet Explorer. This period is known as the ",[32,33,38],"a",{"href":34,"rel":35,"target":37},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FBrowser_wars",[36],"nofollow","_blank","browser wars",". To stay ahead, Netscape wanted pages that reacted to what people did instead of just showing text and images.",[17,41,42,43,48],{},"That called for a scripting language: a simple language for small programs that run inside the page itself. Netscape hired ",[32,44,47],{"href":45,"rel":46,"target":37},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJavaScript#History",[36],"Brendan Eich"," to embed Scheme, a language with academic roots, in the browser. Then management changed the request: the new language had to look like Java, which was everywhere at the time. And the deadline was extremely short.",[24,50,52],{"id":51},"how-long-did-brendan-eich-take-to-create-javascript","How long did Brendan Eich take to create JavaScript?",[17,54,55,56,61,62,66],{},"The first version took about ",[32,57,60],{"href":58,"rel":59,"target":37},"https:\u002F\u002Fbuttondown.com\u002Fhillelwayne\u002Farchive\u002Fdid-brendan-eich-really-make-javascript-in-10-days\u002F",[36],"10 days",", in May 1995. The prototype was called ",[63,64,65],"strong",{},"Mocha"," and was meant to be an internal demo only.",[17,68,69],{},"One caveat matters here. The 10 days apply to the prototype, not to the language as we know it. It kept being reshaped over the following months and kept evolving for decades. Still, the rush of those first days explains several oddities that survive today.",[24,71,73,74,78,79,82],{"id":72},"why-does-typeof-null-return-object","Why does ",[75,76,77],"code",{},"typeof null"," return ",[75,80,81],{},"\"object\"","?",[17,84,85],{},"The most famous example of those leftovers is in any browser console:",[87,88,93],"pre",{"className":89,"code":90,"language":91,"meta":92,"style":92},"language-js shiki shiki-themes dracula","typeof null; \u002F\u002F \"object\"\n","js","",[75,94,95],{"__ignoreMap":92},[96,97,100,104,108,112],"span",{"class":98,"line":99},"line",1,[96,101,103],{"class":102},"s0Tla","typeof",[96,105,107],{"class":106},"sIQBb"," null",[96,109,111],{"class":110},"sCdxs","; ",[96,113,115],{"class":114},"shSDL","\u002F\u002F \"object\"\n",[17,117,118,121,122,127],{},[75,119,120],{},"null"," is not an object. That result is a ",[32,123,126],{"href":124,"rel":125,"target":37},"https:\u002F\u002Falexanderell.is\u002Fposts\u002Ftypeof-null\u002F",[36],"bug from the first implementation",", tied to the type tag the language used to identify objects. It was never fixed because fixing it now would break existing code that relies on it. A fix was proposed and rejected for exactly that reason.",[24,129,131],{"id":130},"from-mocha-to-livescript-to-javascript-how-the-name-changed-twice","From Mocha to LiveScript to JavaScript: how the name changed twice",[17,133,134,135,138],{},"In September 1995, the language shipped in a beta of Navigator 2.0 under a different name, ",[63,136,137],{},"LiveScript",". At that moment Sun Microsystems' Java was the most talked-about language in the world, and Netscape had just struck a partnership with Sun.",[17,140,141,142,145],{},"In December 1995, the two companies announced together that LiveScript would be renamed ",[63,143,144],{},"JavaScript",". Brendan Eich himself described the change as a marketing move by Netscape to ride Java's fame. It worked, and the name stuck. It also created a confusion that lasts to this day.",[24,147,149],{"id":148},"are-java-and-javascript-the-same-thing","Are Java and JavaScript the same thing?",[17,151,152,153,156,157,160],{},"No. Looking at the code, the two look like cousins: curly braces, semicolons, ",[75,154,155],{},"if"," and ",[75,158,159],{},"for",". Under the hood they are very different.",[162,163,164,178],"table",{},[165,166,167],"thead",{},[168,169,170,173,176],"tr",{},[171,172],"th",{},[171,174,175],{},"Java",[171,177,144],{},[179,180,181,193,204],"tbody",{},[168,182,183,187,190],{},[184,185,186],"td",{},"Execution",[184,188,189],{},"Compiled, runs on a virtual machine (the JVM)",[184,191,192],{},"Born to run inside the browser",[168,194,195,198,201],{},[184,196,197],{},"Types",[184,199,200],{},"You declare the type of each variable",[184,202,203],{},"The type is decided on the fly, at runtime",[168,205,206,209,212],{},[184,207,208],{},"Inheritance",[184,210,211],{},"Class-based",[184,213,214],{},"Prototype-based",[87,216,220],{"className":217,"code":218,"language":219,"meta":92,"style":92},"language-java shiki shiki-themes dracula","int age = 30; \u002F\u002F Java: the type is required\n","java",[75,221,222],{"__ignoreMap":92},[96,223,224],{"class":98,"line":99},[96,225,218],{},[87,227,229],{"className":89,"code":228,"language":91,"meta":92,"style":92},"let age = 30; \u002F\u002F JavaScript: the type comes from the value\n",[75,230,231],{"__ignoreMap":92},[96,232,233,236,239,242,245,247],{"class":98,"line":99},[96,234,235],{"class":102},"let",[96,237,238],{"class":110}," age ",[96,240,241],{"class":102},"=",[96,243,244],{"class":106}," 30",[96,246,111],{"class":110},[96,248,249],{"class":114},"\u002F\u002F JavaScript: the type comes from the value\n",[17,251,252],{},"The relationship is the same as between \"ham\" and \"hamster\": the words start the same and the resemblance ends there. The similar name became a problem for Netscape's competitors, and that pushed Microsoft into its next move.",[24,254,256],{"id":255},"what-are-jscript-and-ecmascript","What are JScript and ECMAScript?",[17,258,259,260,263],{},"When Microsoft wanted the same language in Internet Explorer, it could not use the name JavaScript, because the trademark belonged to Sun. In 1996 it shipped its own version in Internet Explorer 3, called ",[63,261,262],{},"JScript",", with differences from Netscape's. Two different versions of the same language were a recipe for broken websites.",[17,265,266,267,270,271,276],{},"To fix that, Netscape took the language to Ecma, a standards organization, in November 1996. The first edition of the standard, ECMA-262, came out in June 1997 under a name nobody liked: ",[63,268,269],{},"ECMAScript",". The name was a political compromise between the companies involved. Brendan Eich once said, ",[32,272,275],{"href":273,"rel":274,"target":37},"https:\u002F\u002Fbugzilla.mozilla.org\u002Fshow_bug.cgi?id=44841",[36],"in a Mozilla Bugzilla comment",", that it sounded like a skin disease.",[17,278,279],{},"That is where the \"ES\" in ES6 comes from: ES6 is ECMAScript 2015, published in June 2015. Since then the standard has had one edition per year. The standard solved the technical confusion, but not the trademark question.",[24,281,283],{"id":282},"who-owns-the-name-javascript-today","Who owns the name JavaScript today?",[17,285,286,287,292],{},"The \"JavaScript\" trademark was registered by Sun. When Oracle ",[32,288,291],{"href":289,"rel":290,"target":37},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FSun_acquisition_by_Oracle",[36],"bought Sun",", in January 2010, it inherited the registration. Oracle does not sell any product called JavaScript.",[17,294,295,296,301,302,307],{},"In September 2024, an ",[32,297,300],{"href":298,"rel":299,"target":37},"https:\u002F\u002Fdeno.com\u002Fblog\u002Fjavascript-tm-gofundme",[36],"open letter"," led by Ryan Dahl, creator of Node.js and Deno, asked Oracle to release the name. It passed 14,000 signatures, including Brendan Eich's. Oracle refused, and in November 2024 Deno ",[32,303,306],{"href":304,"rel":305,"target":37},"https:\u002F\u002Fdeno.com\u002Fblog\u002Fdeno-v-oracle3",[36],"asked the USPTO",", the US trademark office, to cancel the registration.",[17,309,310],{},"Here is how the case has moved:",[312,313,314,324,333,336],"ul",{},[315,316,317,318,323],"li",{},"In June 2025, the Trademark Trial and Appeal Board (TTAB) ",[32,319,322],{"href":320,"rel":321,"target":37},"https:\u002F\u002Fdeno.com\u002Fblog\u002Fdeno-v-oracle4",[36],"dismissed Deno's fraud claim",". The arguments that the name is generic and that the mark was abandoned remained.",[315,325,326,327,332],{},"In August 2025, Oracle ",[32,328,331],{"href":329,"rel":330,"target":37},"https:\u002F\u002Fwww.infoworld.com\u002Farticle\u002F3818005\u002Foracle-maintains-hold-on-javascript-trademark.html",[36],"filed its answer",", denying those arguments.",[315,334,335],{},"Throughout 2026, the parties kept requesting deadline extensions.",[315,337,338],{},"On August 28, 2026, the case was suspended at Oracle's request, with Deno's consent, while the two sides negotiate a settlement.",[17,340,341,344,345,350],{},[63,342,343],{},"Status checked on November 5, 2026:"," in the ",[32,346,349],{"href":347,"rel":348,"target":37},"https:\u002F\u002Fttabvue.uspto.gov\u002Fttabvue\u002Fv?pno=92086835&pty=CAN",[36],"public TTAB record"," (proceeding 92086835), the case is still suspended pending settlement negotiations, with no final decision. This text will go out of date if that changes, so check the link in the sources.",[24,352,354],{"id":353},"in-short","In short",[17,356,357],{},"JavaScript is called JavaScript because of marketing. In December 1995, Netscape renamed LiveScript to ride the success of Java, in a partnership with Sun. The languages are different under the hood, and the borrowed name was never given back: today the trademark belongs to Oracle, which inherited it from Sun, and the future of the name sits in a case suspended for settlement talks.",[17,359,360,361,366],{},"This article is part of the series \"O nome por trás do código\" (The Name Behind the Code), on the YouTube channel. You can find it ",[32,362,365],{"href":363,"rel":364},"https:\u002F\u002Fwww.youtube.com\u002Fplaylist?list=PLUhXtxqxMwPw",[36],"on the playlist",".",[24,368,370],{"id":369},"sources","Sources",[312,372,373,380,387,394,401,408,414,421,428,435,442],{},[315,374,375,379],{},[32,376,144],{"href":377,"rel":378,"target":37},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FJavaScript",[36]," (Wikipedia, History section)",[315,381,382,386],{},[32,383,385],{"href":34,"rel":384,"target":37},[36],"Browser wars"," (Wikipedia)",[315,388,389,393],{},[32,390,392],{"href":58,"rel":391,"target":37},[36],"Did Brendan Eich really make JavaScript in 10 days?"," (Hillel Wayne)",[315,395,396,400],{},[32,397,399],{"href":124,"rel":398,"target":37},[36],"typeof null: investigating a classic JavaScript bug"," (alexanderell.is)",[315,402,403,407],{},[32,404,406],{"href":273,"rel":405,"target":37},[36],"Brendan Eich's comment on the ECMAScript name"," (Mozilla Bugzilla, 2006)",[315,409,410,386],{},[32,411,413],{"href":289,"rel":412,"target":37},[36],"Sun acquisition by Oracle",[315,415,416,420],{},[32,417,419],{"href":298,"rel":418,"target":37},[36],"Help us raise $200k to free the JavaScript trademark"," (Deno, 2024)",[315,422,423,427],{},[32,424,426],{"href":304,"rel":425,"target":37},[36],"Deno v Oracle Update 3"," (Deno, 2025)",[315,429,430,434],{},[32,431,433],{"href":320,"rel":432,"target":37},[36],"JavaScript Trademark Update"," (Deno, June 2025)",[315,436,437,441],{},[32,438,440],{"href":329,"rel":439,"target":37},[36],"Oracle maintains hold on JavaScript trademark"," (InfoWorld, 2025)",[315,443,444,448],{},[32,445,447],{"href":347,"rel":446,"target":37},[36],"Cancellation 92086835, Deno Land Inc. v. Oracle America, Inc."," (TTABVUE, USPTO), accessed November 5, 2026",[450,451,452],"style",{},"html pre.shiki code .s0Tla, html code.shiki .s0Tla{--shiki-default:#FF79C6}html pre.shiki code .sIQBb, html code.shiki .sIQBb{--shiki-default:#BD93F9}html pre.shiki code .sCdxs, html code.shiki .sCdxs{--shiki-default:#F8F8F2}html pre.shiki code .shSDL, html code.shiki .shSDL{--shiki-default:#6272A4}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":92,"searchDepth":454,"depth":454,"links":455},2,[456,457,458,460,461,462,463,464,465],{"id":26,"depth":454,"text":27},{"id":51,"depth":454,"text":52},{"id":72,"depth":454,"text":459},"Why does typeof null return \"object\"?",{"id":130,"depth":454,"text":131},{"id":148,"depth":454,"text":149},{"id":255,"depth":454,"text":256},{"id":282,"depth":454,"text":283},{"id":353,"depth":454,"text":354},{"id":369,"depth":454,"text":370},[467,468],"historia-da-programacao","javascript","JavaScript has nothing to do with Java. See how the browser wars, 10 days of rush and a 1995 deal with Sun gave the language its name, and who owns it today.","why-is-javascript-called-javascript","md",{},true,"\u002Fen\u002Fblog\u002Fwhy-is-javascript-called-javascript","por-que-javascript-se-chama-javascript","2026-11-05",{"title":6,"description":469},{"loc":474,"lastmod":476},"en\u002Fblog\u002Fwhy-is-javascript-called-javascript",1248,"wJ4xDRQaewog286B81R8ZU_xl4j0_42sVy4JkOLyfQQ",{"id":483,"title":484,"articleCover":485,"articleCoverEmbed":486,"articleCoverEmbedUploadDate":486,"articleCoverHeight":10,"articleCoverWidth":11,"authorId":12,"body":487,"categoriesIds":792,"description":793,"enAlternateSlug":794,"extension":471,"meta":795,"navigation":473,"path":796,"ptBrAlternateSlug":797,"publishedAt":476,"seo":798,"sitemap":799,"stem":800,"updatedAt":476,"wordCount":801,"__hash__":802},"blogEn\u002Fen\u002Fblog\u002Fwhy-is-linux-called-linux.md","Why Is Linux Called Linux? The Story of Freax, Unix and GNU","\u002Fimages\u002Fen\u002Fblog\u002Fhistoria-da-programacao\u002Fwhy-is-linux-called-linux.webp",null,{"type":14,"value":488,"toc":780},[489,492,495,499,512,515,519,532,539,543,559,562,566,573,586,593,604,613,617,630,633,637,650,654,657,666,669,673,682,684,690,698,700,777],[17,490,491],{},"Linux runs your Android phone, sits behind most websites and powers the fastest supercomputers in the world. Yet its creator didn't want that name. Linus Torvalds thought \"Linux\" was too self-centered and picked another one: Freax.",[17,493,494],{},"So how did the name Linux end up on the project? The answer involves a 1960s operating system, an annoyed professor, a volunteer who created a folder on a server in Finland and a trademark fight.",[24,496,498],{"id":497},"where-does-the-x-in-linux-come-from","Where does the \"x\" in Linux come from?",[17,500,501,502,505,506,511],{},"The \"x\" comes from ",[63,503,504],{},"Unix",", an operating system created in 1969 at Bell Labs (AT&T) by Ken Thompson, Dennis Ritchie and colleagues, according to the ",[32,507,510],{"href":508,"rel":509},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHistory_of_Unix",[36],"history of Unix",". An operating system is the program that makes a computer work, like Windows. The name Unix, originally \"Unics\", was a pun on Multics, a large and complicated system Bell Labs had walked away from.",[17,513,514],{},"Unix was a hit, but AT&T licensed the code, the licenses got expensive and nobody could freely redistribute it. In the video I compare it to a rented car: expensive, with the hood locked. The \"x\" became a kind of family name for systems inspired by it, such as Minix and Linux.",[24,516,518],{"id":517},"what-is-gnu-and-why-didnt-it-have-a-kernel","What is GNU and why didn't it have a kernel?",[17,520,521,522,531],{},"On September 27, 1983, Richard Stallman ",[32,523,526,527,530],{"href":524,"rel":525},"https:\u002F\u002Fwww.gnu.org\u002Fgnu\u002Finitial-announcement.html",[36],"announced the ",[63,528,529],{},"GNU"," project",", aiming to build a free system that anyone could open, study and modify. The name is a joke: GNU stands for \"GNU's Not Unix\", an acronym that contains itself.",[17,533,534,535,538],{},"By 1991 GNU had almost everything a complete system needs: the GCC compiler (1987), which turns code into programs, the Emacs editor and the Bash shell (1989). What it lacked was a ",[63,536,537],{},"kernel",", the core that talks to the computer's parts (memory, disk, keyboard). GNU's kernel, Hurd, wasn't ready. In the car metaphor, GNU had built nearly the whole car, but the engine was missing.",[24,540,542],{"id":541},"who-created-linux-and-why","Who created Linux and why?",[17,544,545,546,553,554,558],{},"The engine came from an unexpected place. In January 1991, Linus Torvalds, a 21-year-old student at the University of Helsinki, bought a PC with a 386 processor. He ran ",[32,547,550],{"href":548,"rel":549},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FMinix",[36],[63,551,552],{},"Minix",", a system professor Andrew Tanenbaum ",[32,555,557],{"href":548,"rel":556},[36],"had created in 1987"," for teaching, whose license restricted redistribution.",[17,560,561],{},"Frustrated by those limits, Linus started writing his own kernel just for fun. On August 25, 1991, he told the comp.os.minix newsgroup that he was making a free operating system, and that it was just a hobby, not going to be big and professional like GNU. One thing was missing before he could publish the code: a name.",[24,563,565],{"id":564},"why-was-linux-almost-called-freax","Why was Linux almost called Freax?",[17,567,568,569,572],{},"The name Linus wanted was ",[63,570,571],{},"Freax",", a mix of \"free\", \"freak\" and the \"x\" from Unix. He had thought about \"Linux\", but found it too presumptuous, since it carried his own first name.",[17,574,575,576,581,582,585],{},"In September 1991 he uploaded the code to ftp.funet.fi (the ",[32,577,580],{"href":578,"rel":579},"https:\u002F\u002Flinuxiac.com\u002Flinux-birthday\u002F",[36],"story is on Linuxiac","), the server where Finnish universities kept programs for download. Ari Lemmke, one of the server's volunteer administrators, didn't like \"Freax\" and created the folder under a different name: ",[75,583,584],{},"linux",". Version 0.01 was published there on September 17, 1991. Linus didn't fight it, and the name stuck.",[17,587,588,589,592],{},"Freax didn't vanish completely. It survives in a comment in the kernel's ",[75,590,591],{},"Makefile"," from that first version:",[87,594,598],{"className":595,"code":596,"language":597,"meta":92,"style":92},"language-makefile shiki shiki-themes dracula","# Makefile for the FREAX-kernel.\n","makefile",[75,599,600],{"__ignoreMap":92},[96,601,602],{"class":98,"line":99},[96,603,596],{},[17,605,606,607,612],{},"I checked that line on November 5, 2026, against a copy of version 0.01. Linus also ",[32,608,611],{"href":609,"rel":610},"https:\u002F\u002Fcommons.wikimedia.org\u002Fwiki\u002FFile:Linus-linux.ogg",[36],"recorded an audio clip"," explaining how he pronounces the name (roughly \"LIN-ux\", with a short \"i\"), which circulated with the code.",[24,614,616],{"id":615},"what-was-the-tanenbaumtorvalds-debate","What was the Tanenbaum–Torvalds debate?",[17,618,619,620,625,626,629],{},"The first public fight came along with the name (the ",[32,621,624],{"href":622,"rel":623},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FTanenbaum%E2%80%93Torvalds_debate",[36],"Tanenbaum–Torvalds debate","). In January 1992, the professor who created Minix posted a message titled \"LINUX is obsolete\". His technical argument was that a modern kernel should be a ",[63,627,628],{},"microkernel",", an engine made of separate parts, while Linux's was monolithic, a single block. Tanenbaum even joked that Linus wouldn't get a high grade if he were his student.",[17,631,632],{},"While that argument went on, Linux changed license. Starting with version 0.12, in 1992, it adopted the GPL, the GNU license that lets anyone use, change and share the code. Before that, its own license forbade commercial use. With the GPL, Linus's kernel fit into the GNU system, and the car finally ran.",[24,634,636],{"id":635},"linux-or-gnulinux","Linux or GNU\u002FLinux?",[17,638,639,640,645,646,649],{},"Because the system people actually use is the kernel plus the GNU tools, the Free Software Foundation and Richard Stallman ",[32,641,644],{"href":642,"rel":643},"https:\u002F\u002Fwww.gnu.org\u002Fgnu\u002Fgnu-linux-faq.html",[36],"still ask"," that the complete system be called ",[63,647,648],{},"GNU\u002FLinux",". Most people just say Linux, which strictly names the kernel. Both forms show up in books and distributions.",[24,651,653],{"id":652},"who-owns-the-linux-trademark","Who owns the Linux trademark?",[17,655,656],{},"The most dangerous fight over the name came from outside. In 1995, an American named William R. Della Croce Jr. registered \"Linux\" as a trademark in the United States without having written a line of the code, and began demanding 10% royalties from people selling Linux on CDs. The community and several companies went to court, and in 1997 the trademark was transferred to Linus Torvalds.",[17,658,659,660,665],{},"I checked on November 5, 2026: according to the ",[32,661,664],{"href":662,"rel":663},"https:\u002F\u002Fwww.linuxfoundation.org\u002Flegal\u002Fthe-linux-mark",[36],"Linux Foundation",", Linus is still the worldwide owner of the mark, and the foundation is the exclusive licensee that handles sublicensing. The name he thought was too presumptuous is his, by law.",[17,667,668],{},"Unix is a registered trademark too, held by The Open Group, which runs its own certification. Almost no Linux distribution carries the badge, though there are exceptions, such as Huawei's EulerOS.",[24,670,672],{"id":671},"what-came-after-linux","What came after Linux?",[17,674,675,676,681],{},"Fourteen years after Linux, in April 2005, Linus created another tool and this time picked the name himself: ",[32,677,680],{"href":678,"rel":679},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FGit",[36],"Git",". The story behind that name is the next episode of the series.",[24,683,354],{"id":353},[17,685,686,687,689],{},"Linux has that name because the person who ran the server where it was first published didn't like Freax and created a folder called ",[75,688,584],{},". Linus Torvalds found the name too presumptuous, but it stuck, and the \"x\" tied the system to the Unix family. The name later survived an attempt by an outsider to register it and returned to Linus in 1997. Today he owns the trademark, and the Linux Foundation looks after it.",[17,691,692,693,697],{},"This article is part of the series ",[32,694,696],{"href":363,"rel":695},[36],"The name behind the code"," on the Código ao Ponto channel, in Portuguese.",[24,699,370],{"id":369},[312,701,702,708,715,727,734,739,744,751,758,765,772],{},[315,703,704,386],{},[32,705,707],{"href":508,"rel":706},[36],"History of Unix",[315,709,710,714],{},[32,711,713],{"href":524,"rel":712},[36],"Initial announcement of the GNU project"," (gnu.org, 1983)",[315,716,717,156,722,726],{},[32,718,721],{"href":719,"rel":720},"https:\u002F\u002Fwww.gnu.org\u002Fgnu\u002Flinux-and-gnu.html",[36],"Linux and the GNU System",[32,723,725],{"href":642,"rel":724},[36],"GNU\u002FLinux FAQ"," (gnu.org)",[315,728,729,386],{},[32,730,733],{"href":731,"rel":732},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FHistory_of_Linux",[36],"History of Linux",[315,735,736,386],{},[32,737,552],{"href":548,"rel":738},[36],[315,740,741,386],{},[32,742,624],{"href":622,"rel":743},[36],[315,745,746,750],{},[32,747,749],{"href":578,"rel":748},[36],"Linux's real birthday"," (Linuxiac), with the Makefile comment",[315,752,753,386],{},[32,754,757],{"href":755,"rel":756},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FLinux#Trademark",[36],"Linux, Trademark section",[315,759,760,764],{},[32,761,763],{"href":662,"rel":762},[36],"The Linux Mark"," (Linux Foundation, checked on 11\u002F05\u002F2026)",[315,766,767,771],{},[32,768,770],{"href":609,"rel":769},[36],"Linus-linux.ogg"," (Wikimedia Commons)",[315,773,774,386],{},[32,775,680],{"href":678,"rel":776},[36],[450,778,779],{},"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":92,"searchDepth":454,"depth":454,"links":781},[782,783,784,785,786,787,788,789,790,791],{"id":497,"depth":454,"text":498},{"id":517,"depth":454,"text":518},{"id":541,"depth":454,"text":542},{"id":564,"depth":454,"text":565},{"id":615,"depth":454,"text":616},{"id":635,"depth":454,"text":636},{"id":652,"depth":454,"text":653},{"id":671,"depth":454,"text":672},{"id":353,"depth":454,"text":354},{"id":369,"depth":454,"text":370},[467],"Linus Torvalds wanted to name Linux Freax. Find out why the name changed, what the x has to do with Unix, and how the Linux trademark ended up with him.","why-is-linux-called-linux",{},"\u002Fen\u002Fblog\u002Fwhy-is-linux-called-linux","por-que-linux-se-chama-linux",{"title":484,"description":793},{"loc":796,"lastmod":476},"en\u002Fblog\u002Fwhy-is-linux-called-linux",1188,"W0pOKNsa_7nTHxNAG3CidNelW6MWTdgo_JScNKErqqw",{"id":804,"title":805,"articleCover":806,"articleCoverEmbed":486,"articleCoverEmbedUploadDate":486,"articleCoverHeight":10,"articleCoverWidth":11,"authorId":12,"body":807,"categoriesIds":1829,"description":1832,"enAlternateSlug":1833,"extension":471,"meta":1834,"navigation":473,"path":1835,"ptBrAlternateSlug":1836,"publishedAt":1837,"seo":1838,"sitemap":1839,"stem":1840,"updatedAt":1837,"wordCount":1841,"__hash__":1842},"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",{"type":14,"value":808,"toc":1803},[809,824,830,834,837,840,843,848,875,879,890,1052,1059,1074,1078,1089,1159,1162,1166,1173,1230,1239,1243,1249,1301,1315,1318,1383,1394,1397,1401,1404,1465,1474,1481,1484,1491,1505,1539,1546,1550,1553,1669,1684,1688,1691,1698,1702,1705,1708,1725,1729,1733,1736,1740,1748,1752,1757,1761,1770,1774,1777,1781,1793,1797,1800],[17,810,811,812,815,816,819,820,823],{},"Vue 3.6 is not stable yet. As of October 6, 2026, the latest version is ",[75,813,814],{},"3.6.0-rc.10"," (September 30), and the stable release is still ",[75,817,818],{},"3.5.43"," (September 17). I checked both on npm and in the ",[75,821,822],{},"vuejs\u002Fcore"," releases on that date.",[17,825,826,829],{},[63,827,828],{},"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.",[24,831,833],{"id":832},"how-vapor-works","How Vapor works",[17,835,836],{},"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.",[17,838,839],{},"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.",[17,841,842],{},"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.",[844,845,847],"h3",{"id":846},"what-about-alien-signals","What about Alien Signals?",[17,849,850,851,854,855,860,861,864,865,864,868,156,871,874],{},"Vue 3.6 also ships a major refactor of ",[75,852,853],{},"@vue\u002Freactivity"," based on the ",[32,856,859],{"href":857,"rel":858},"https:\u002F\u002Fgithub.com\u002Fstackblitz\u002Falien-signals",[36],"alien-signals"," library. The rc.1 notes say it improves the reactivity system's performance and memory usage. It's an internal change: ",[75,862,863],{},"ref",", ",[75,866,867],{},"reactive",[75,869,870],{},"computed",[75,872,873],{},"watch"," keep the same API. Since it's part of Vue 3.6, you get it whether or not you use Vapor.",[24,876,878],{"id":877},"how-to-enable-vapor-mode","How to enable Vapor Mode",[17,880,881,882,885,886,889],{},"Vapor works with SFCs that use ",[75,883,884],{},"\u003Cscript setup>"," or only a template. You mark the component with the ",[75,887,888],{},"vapor"," attribute:",[87,891,895],{"className":892,"code":893,"language":894,"meta":92,"style":92},"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",[75,896,897,930,949,955,979,989,994,1004,1027,1033,1043],{"__ignoreMap":92},[96,898,899,902,905,909,912,915,917,921,925,927],{"class":98,"line":99},[96,900,901],{"class":110},"\u003C",[96,903,904],{"class":102},"script",[96,906,908],{"class":907},"sY_PY"," setup",[96,910,911],{"class":907}," vapor",[96,913,914],{"class":907}," lang",[96,916,241],{"class":102},[96,918,920],{"class":919},"seVfx","\"",[96,922,924],{"class":923},"s-mGx","ts",[96,926,920],{"class":919},[96,928,929],{"class":110},">\n",[96,931,932,935,938,941,944,946],{"class":98,"line":454},[96,933,934],{"class":102},"import",[96,936,937],{"class":110}," { ref } ",[96,939,940],{"class":102},"from",[96,942,943],{"class":919}," '",[96,945,894],{"class":923},[96,947,948],{"class":919},"'\n",[96,950,952],{"class":98,"line":951},3,[96,953,954],{"emptyLinePlaceholder":473},"\n",[96,956,958,961,964,966,970,973,976],{"class":98,"line":957},4,[96,959,960],{"class":102},"const",[96,962,963],{"class":110}," count ",[96,965,241],{"class":102},[96,967,969],{"class":968},"sAOxA"," ref",[96,971,972],{"class":110},"(",[96,974,975],{"class":106},"0",[96,977,978],{"class":110},")\n",[96,980,982,985,987],{"class":98,"line":981},5,[96,983,984],{"class":110},"\u003C\u002F",[96,986,904],{"class":102},[96,988,929],{"class":110},[96,990,992],{"class":98,"line":991},6,[96,993,954],{"emptyLinePlaceholder":473},[96,995,997,999,1002],{"class":98,"line":996},7,[96,998,901],{"class":110},[96,1000,1001],{"class":102},"template",[96,1003,929],{"class":110},[96,1005,1007,1010,1013,1016,1018,1020,1023,1025],{"class":98,"line":1006},8,[96,1008,1009],{"class":110},"  \u003C",[96,1011,1012],{"class":102},"button",[96,1014,1015],{"class":907}," @click",[96,1017,241],{"class":102},[96,1019,920],{"class":919},[96,1021,1022],{"class":923},"count++",[96,1024,920],{"class":919},[96,1026,929],{"class":110},[96,1028,1030],{"class":98,"line":1029},9,[96,1031,1032],{"class":110},"    count is {{ count }}\n",[96,1034,1036,1039,1041],{"class":98,"line":1035},10,[96,1037,1038],{"class":110},"  \u003C\u002F",[96,1040,1012],{"class":102},[96,1042,929],{"class":110},[96,1044,1046,1048,1050],{"class":98,"line":1045},11,[96,1047,984],{"class":110},[96,1049,1001],{"class":102},[96,1051,929],{"class":110},[17,1053,1054,1055,1058],{},"Outside Nuxt, ",[75,1056,1057],{},"@vitejs\u002Fplugin-vue"," compiles Vapor SFCs (the plugin's changelog records Vapor component support, including template-only ones), so use a recent version.",[17,1060,1061,1062,1065,1066,1069,1070,1073],{},"Vue also accepts ",[75,1063,1064],{},"\u003Cscript vapor>"," as shorthand for ",[75,1067,1068],{},"\u003Cscript setup vapor>",". You can instead put the marker on the template with ",[75,1071,1072],{},"\u003Ctemplate vapor>"," to compile the whole SFC in Vapor mode.",[844,1075,1077],{"id":1076},"vapor-components-in-an-existing-app","Vapor components in an existing app",[17,1079,1080,1081,1084,1085,1088],{},"In an app created with ",[75,1082,1083],{},"createApp()",", you need to install ",[75,1086,1087],{},"vaporInteropPlugin"," to use Vapor components:",[87,1090,1093],{"className":1091,"code":1092,"language":924,"meta":92,"style":92},"language-ts shiki shiki-themes dracula","import { createApp, vaporInteropPlugin } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateApp(App).use(vaporInteropPlugin).mount('#app')\n",[75,1094,1095,1110,1126,1130],{"__ignoreMap":92},[96,1096,1097,1099,1102,1104,1106,1108],{"class":98,"line":99},[96,1098,934],{"class":102},[96,1100,1101],{"class":110}," { createApp, vaporInteropPlugin } ",[96,1103,940],{"class":102},[96,1105,943],{"class":919},[96,1107,894],{"class":923},[96,1109,948],{"class":919},[96,1111,1112,1114,1117,1119,1121,1124],{"class":98,"line":454},[96,1113,934],{"class":102},[96,1115,1116],{"class":110}," App ",[96,1118,940],{"class":102},[96,1120,943],{"class":919},[96,1122,1123],{"class":923},".\u002FApp.vue",[96,1125,948],{"class":919},[96,1127,1128],{"class":98,"line":951},[96,1129,954],{"emptyLinePlaceholder":473},[96,1131,1132,1135,1138,1141,1144,1147,1149,1152,1155,1157],{"class":98,"line":957},[96,1133,1134],{"class":968},"createApp",[96,1136,1137],{"class":110},"(App).",[96,1139,1140],{"class":968},"use",[96,1142,1143],{"class":110},"(vaporInteropPlugin).",[96,1145,1146],{"class":968},"mount",[96,1148,972],{"class":110},[96,1150,1151],{"class":919},"'",[96,1153,1154],{"class":923},"#app",[96,1156,1151],{"class":919},[96,1158,978],{"class":110},[17,1160,1161],{},"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.",[844,1163,1165],{"id":1164},"a-fully-vapor-app","A fully Vapor app",[17,1167,1168,1169,1172],{},"If every component is Vapor, use ",[75,1170,1171],{},"createVaporApp()",":",[87,1174,1176],{"className":1091,"code":1175,"language":924,"meta":92,"style":92},"import { createVaporApp } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateVaporApp(App).mount('#app')\n",[75,1177,1178,1193,1207,1211],{"__ignoreMap":92},[96,1179,1180,1182,1185,1187,1189,1191],{"class":98,"line":99},[96,1181,934],{"class":102},[96,1183,1184],{"class":110}," { createVaporApp } ",[96,1186,940],{"class":102},[96,1188,943],{"class":919},[96,1190,894],{"class":923},[96,1192,948],{"class":919},[96,1194,1195,1197,1199,1201,1203,1205],{"class":98,"line":454},[96,1196,934],{"class":102},[96,1198,1116],{"class":110},[96,1200,940],{"class":102},[96,1202,943],{"class":919},[96,1204,1123],{"class":923},[96,1206,948],{"class":919},[96,1208,1209],{"class":98,"line":951},[96,1210,954],{"emptyLinePlaceholder":473},[96,1212,1213,1216,1218,1220,1222,1224,1226,1228],{"class":98,"line":957},[96,1214,1215],{"class":968},"createVaporApp",[96,1217,1137],{"class":110},[96,1219,1146],{"class":968},[96,1221,972],{"class":110},[96,1223,1151],{"class":919},[96,1225,1154],{"class":923},[96,1227,1151],{"class":919},[96,1229,978],{"class":110},[17,1231,1232,1233,1235,1236,1238],{},"The Virtual DOM runtime then stays out of the bundle. Each component still needs the ",[75,1234,888],{}," marker. Components written with render functions or JSX remain Virtual DOM components and need interop. A Vapor app can also install ",[75,1237,1087],{}," to accept Virtual DOM components, but that pulls the runtime back in and cuts into the bundle savings.",[24,1240,1242],{"id":1241},"vapor-in-nuxt-46","Vapor in Nuxt 4.6",[17,1244,1245,1246,1172],{},"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 ",[75,1247,1248],{},"nuxt.config.ts",[87,1250,1252],{"className":1091,"code":1251,"language":924,"meta":92,"style":92},"export default defineNuxtConfig({\n  vue: {\n    vapor: true,\n  },\n})\n",[75,1253,1254,1268,1278,1291,1296],{"__ignoreMap":92},[96,1255,1256,1259,1262,1265],{"class":98,"line":99},[96,1257,1258],{"class":102},"export",[96,1260,1261],{"class":102}," default",[96,1263,1264],{"class":968}," defineNuxtConfig",[96,1266,1267],{"class":110},"({\n",[96,1269,1270,1273,1275],{"class":98,"line":454},[96,1271,1272],{"class":110},"  vue",[96,1274,1172],{"class":102},[96,1276,1277],{"class":110}," {\n",[96,1279,1280,1283,1285,1288],{"class":98,"line":951},[96,1281,1282],{"class":110},"    vapor",[96,1284,1172],{"class":102},[96,1286,1287],{"class":106}," true",[96,1289,1290],{"class":110},",\n",[96,1292,1293],{"class":98,"line":957},[96,1294,1295],{"class":110},"  },\n",[96,1297,1298],{"class":98,"line":981},[96,1299,1300],{"class":110},"})\n",[17,1302,1303,1304,1306,1307,1310,1311,1314],{},"Then mark components with ",[75,1305,1068],{},", as in the example above. Nuxt requires Vue ",[75,1308,1309],{},"^3.6.0-rc.2"," or newer. According to the release post, routing, ",[75,1312,1313],{},"useAsyncData",", layouts and most built-in components keep working unchanged. The post recommends trying Vapor in a fresh project first.",[17,1316,1317],{},"The Nuxt docs list a few limitations of interop mode:",[312,1319,1320,1323,1330,1350,1357,1376],{},[315,1321,1322],{},"the app root can't be Vapor, because fully Vapor apps aren't available in Nuxt;",[315,1324,1325,1326,1329],{},"Vapor components don't expose ",[75,1327,1328],{},"$el"," on template refs;",[315,1331,1332,1335,1336,1339,1340,864,1343,156,1346,1349],{},[75,1333,1334],{},"\u003CClientOnly>"," skips its attribute forwarding, and ",[75,1337,1338],{},"\u003CNuxtIsland>",", server components and the head components that read text children (",[75,1341,1342],{},"\u003CTitle>",[75,1344,1345],{},"\u003CStyle>",[75,1347,1348],{},"\u003CNoscript>",") warn if you pass them a Vapor slot;",[315,1351,1352,1353,1356],{},"keyed ",[75,1354,1355],{},"onPrehydrate"," falls back to the unkeyed form (and warns in development), because there is no component instance to attach the key to;",[315,1358,1359,156,1362,1365,1366,1369,1370,1372,1373,1375],{},[75,1360,1361],{},"asyncData",[75,1363,1364],{},"fetchKey"," through ",[75,1367,1368],{},"defineNuxtComponent"," don't work (use ",[75,1371,1313],{}," in ",[75,1374,884],{},");",[315,1377,1378,1379,1382],{},"Nuxt composables called after an ",[75,1380,1381],{},"await"," may lose their context.",[17,1384,1385,1386,1389,1390,1393],{},"The same release post has performance numbers for Nuxt 4.6: ",[75,1387,1388],{},"nuxt build"," on a starter project went from 4.4 s to 3.6 s (18% faster), and a page with 300 ",[75,1391,1392],{},"\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.",[17,1395,1396],{},"If you're still on Nuxt 3, it reached end of life on July 31, 2026.",[24,1398,1400],{"id":1399},"what-vapor-doesnt-support-yet","What Vapor doesn't support yet",[17,1402,1403],{},"Vapor supports a subset of Vue's APIs. The official notes list what is unavailable or doesn't apply in Vapor components:",[312,1405,1406,1424,1429,1437,1443,1448],{},[315,1407,1408,1409,864,1412,864,1415,1418,1419,1423],{},"the Options API (",[75,1410,1411],{},"data",[75,1413,1414],{},"methods",[75,1416,1417],{},"mounted"," and so on). If you want a refresher on the difference, see ",[32,1420,1422],{"href":1421},"\u002Fen\u002Fblog\u002Fwhat-is-the-difference-between-composition-api-and-options-api-in-vuejs","Composition API vs Options API",";",[315,1425,1426,1423],{},[75,1427,1428],{},"app.config.globalProperties",[315,1430,1431,1434,1435,1423],{},[75,1432,1433],{},"getCurrentInstance()",", which returns ",[75,1436,120],{},[315,1438,1439,1440,1423],{},"per-element lifecycle events such as ",[75,1441,1442],{},"@vue:mounted",[315,1444,1445,1423],{},[75,1446,1447],{},"v-memo",[315,1449,1450,864,1452,864,1455,864,1458,156,1461,1464],{},[75,1451,1328],{},[75,1453,1454],{},"$props",[75,1456,1457],{},"$attrs",[75,1459,1460],{},"$slots",[75,1462,1463],{},"$refs"," on component template refs.",[17,1466,1467,1468,1470,1471,1473],{},"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 ",[75,1469,1433],{},", the ",[75,1472,120],{}," above could be a problem, but only a test in your own project will tell.",[17,1475,1476,1477,1480],{},"The official notes don't list ",[75,1478,1479],{},"\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.",[17,1482,1483],{},"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.",[844,1485,1487,1488],{"id":1486},"events-and-delegate","Events and ",[75,1489,1490],{},".delegate",[17,1492,1493,1494,1497,1498,1501,1502,1504],{},"In earlier RCs, Vapor delegated events to ",[75,1495,1496],{},"document",", so a ",[75,1499,1500],{},"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 ",[75,1503,1490],{}," modifier:",[87,1506,1508],{"className":892,"code":1507,"language":894,"meta":92,"style":92},"\u003Cbutton @click.delegate=\"onClick\" \u002F>\n",[75,1509,1510],{"__ignoreMap":92},[96,1511,1512,1514,1516,1519,1522,1524,1527,1529,1531,1534,1536],{"class":98,"line":99},[96,1513,901],{"class":110},[96,1515,1012],{"class":102},[96,1517,1518],{"class":110}," @",[96,1520,1521],{"class":907},"click",[96,1523,366],{"class":110},[96,1525,1526],{"class":907},"delegate",[96,1528,241],{"class":102},[96,1530,920],{"class":919},[96,1532,1533],{"class":110},"onClick",[96,1535,920],{"class":919},[96,1537,1538],{"class":110}," \u002F>\n",[17,1540,1541,1542,1545],{},"The ",[75,1543,1544],{},"compilerOptions.eventDelegation"," option was removed.",[844,1547,1549],{"id":1548},"custom-directives","Custom directives",[17,1551,1552],{},"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:",[87,1554,1556],{"className":1091,"code":1555,"language":924,"meta":92,"style":92},"import { watchEffect } from 'vue'\n\nconst MyDirective = (el, source) => {\n  watchEffect(() => {\n    el.textContent = source()\n  })\n  return () => console.log('cleanup')\n}\n",[75,1557,1558,1573,1577,1607,1619,1632,1637,1664],{"__ignoreMap":92},[96,1559,1560,1562,1565,1567,1569,1571],{"class":98,"line":99},[96,1561,934],{"class":102},[96,1563,1564],{"class":110}," { watchEffect } ",[96,1566,940],{"class":102},[96,1568,943],{"class":919},[96,1570,894],{"class":923},[96,1572,948],{"class":919},[96,1574,1575],{"class":98,"line":454},[96,1576,954],{"emptyLinePlaceholder":473},[96,1578,1579,1581,1584,1587,1590,1594,1596,1599,1602,1605],{"class":98,"line":951},[96,1580,960],{"class":102},[96,1582,1583],{"class":968}," MyDirective",[96,1585,1586],{"class":102}," =",[96,1588,1589],{"class":110}," (",[96,1591,1593],{"class":1592},"sGEwX","el",[96,1595,864],{"class":110},[96,1597,1598],{"class":1592},"source",[96,1600,1601],{"class":110},") ",[96,1603,1604],{"class":102},"=>",[96,1606,1277],{"class":110},[96,1608,1609,1612,1615,1617],{"class":98,"line":957},[96,1610,1611],{"class":968},"  watchEffect",[96,1613,1614],{"class":110},"(() ",[96,1616,1604],{"class":102},[96,1618,1277],{"class":110},[96,1620,1621,1624,1626,1629],{"class":98,"line":981},[96,1622,1623],{"class":110},"    el.textContent ",[96,1625,241],{"class":102},[96,1627,1628],{"class":968}," source",[96,1630,1631],{"class":110},"()\n",[96,1633,1634],{"class":98,"line":991},[96,1635,1636],{"class":110},"  })\n",[96,1638,1639,1642,1645,1647,1650,1653,1655,1657,1660,1662],{"class":98,"line":996},[96,1640,1641],{"class":102},"  return",[96,1643,1644],{"class":110}," () ",[96,1646,1604],{"class":102},[96,1648,1649],{"class":110}," console.",[96,1651,1652],{"class":968},"log",[96,1654,972],{"class":110},[96,1656,1151],{"class":919},[96,1658,1659],{"class":923},"cleanup",[96,1661,1151],{"class":919},[96,1663,978],{"class":110},[96,1665,1666],{"class":98,"line":1006},[96,1667,1668],{"class":110},"}\n",[17,1670,1671,1672,1675,1676,1679,1680,366],{},"The full signature is ",[75,1673,1674],{},"(node, value?, argument?, modifiers?)",", where ",[75,1677,1678],{},"value"," is the reactive getter. For a refresher on the traditional model, see ",[32,1681,1683],{"href":1682},"\u002Fen\u002Fblog\u002Fwhat-are-directives-in-vue-and-how-to-implement-them","what directives are in Vue",[24,1685,1687],{"id":1686},"where-vapor-helps","Where Vapor helps",[17,1689,1690],{},"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.",[17,1692,1693,1694,1697],{},"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 ",[75,1695,1696],{},"{{ calculateTotal(item) }}",", is still a bad idea in both modes.",[24,1699,1701],{"id":1700},"should-you-adopt-it-now","Should you adopt it now?",[17,1703,1704],{},"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.",[17,1706,1707],{},"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:",[312,1709,1710,1713,1719,1722],{},[315,1711,1712],{},"you measured current performance (the browser's Performance panel works) and found a rendering bottleneck;",[315,1714,1715,1716,1718],{},"the component uses ",[75,1717,884],{}," or only a template;",[315,1720,1721],{},"the UI libraries it relies on work with interop in your project;",[315,1723,1724],{},"you can isolate Vapor to one route or component and back it out without pain.",[24,1726,1728],{"id":1727},"frequently-asked-questions","Frequently asked questions",[844,1730,1732],{"id":1731},"what-is-vapor-mode-in-vue","What is Vapor Mode in Vue?",[17,1734,1735],{},"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.",[844,1737,1739],{"id":1738},"is-vue-36-stable-yet","Is Vue 3.6 stable yet?",[17,1741,1742,1743,1745,1746,366],{},"Not yet. As of October 6, 2026, the latest version is ",[75,1744,814],{}," and the stable release is ",[75,1747,818],{},[844,1749,1751],{"id":1750},"does-vapor-mode-replace-the-virtual-dom","Does Vapor Mode replace the Virtual DOM?",[17,1753,1754,1755,366],{},"No. It's optional and lives alongside the Virtual DOM in the same app through ",[75,1756,1087],{},[844,1758,1760],{"id":1759},"do-i-need-to-rewrite-my-app-to-use-vapor","Do I need to rewrite my app to use Vapor?",[17,1762,1763,1764,1766,1767,1769],{},"No. You can mark one component at a time with ",[75,1765,888],{},". Components that use the Options API stay out, because Vapor only accepts ",[75,1768,884],{}," and templates.",[844,1771,1773],{"id":1772},"does-vapor-mode-work-with-the-options-api","Does Vapor Mode work with the Options API?",[17,1775,1776],{},"No. The official notes list the Options API as unsupported.",[844,1778,1780],{"id":1779},"does-vapor-mode-work-with-nuxt","Does Vapor Mode work with Nuxt?",[17,1782,1783,1784,1372,1787,1789,1790,1792],{},"Yes, since Nuxt 4.6, in interop mode: set ",[75,1785,1786],{},"vue: { vapor: true }",[75,1788,1248],{}," and mark components with ",[75,1791,1068],{},". The app root stays on the Virtual DOM.",[844,1794,1796],{"id":1795},"is-vapor-mode-faster-than-the-virtual-dom","Is Vapor Mode faster than the Virtual DOM?",[17,1798,1799],{},"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.",[450,1801,1802],{},"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":92,"searchDepth":454,"depth":454,"links":1804},[1805,1808,1812,1813,1818,1819,1820],{"id":832,"depth":454,"text":833,"children":1806},[1807],{"id":846,"depth":951,"text":847},{"id":877,"depth":454,"text":878,"children":1809},[1810,1811],{"id":1076,"depth":951,"text":1077},{"id":1164,"depth":951,"text":1165},{"id":1241,"depth":454,"text":1242},{"id":1399,"depth":454,"text":1400,"children":1814},[1815,1817],{"id":1486,"depth":951,"text":1816},"Events and .delegate",{"id":1548,"depth":951,"text":1549},{"id":1686,"depth":454,"text":1687},{"id":1700,"depth":454,"text":1701},{"id":1727,"depth":454,"text":1728,"children":1821},[1822,1823,1824,1825,1826,1827,1828],{"id":1731,"depth":951,"text":1732},{"id":1738,"depth":951,"text":1739},{"id":1750,"depth":951,"text":1751},{"id":1759,"depth":951,"text":1760},{"id":1772,"depth":951,"text":1773},{"id":1779,"depth":951,"text":1780},{"id":1795,"depth":951,"text":1796},[1830,1831],"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",{},"\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":805,"description":1832},{"loc":1835,"lastmod":1837},"en\u002Fblog\u002Fvue-vapor-mode-what-it-is-and-how-to-use-it",1612,"t7xqEIHrQ1NF0PTd4QIeo_dO6I9PgndCWb5gfcF3uhE",{"id":1844,"title":1845,"articleCover":1846,"articleCoverEmbed":486,"articleCoverEmbedUploadDate":486,"articleCoverHeight":10,"articleCoverWidth":11,"authorId":12,"body":1847,"categoriesIds":3272,"description":3274,"enAlternateSlug":3275,"extension":471,"meta":3276,"navigation":473,"path":3277,"ptBrAlternateSlug":3278,"publishedAt":3279,"seo":3280,"sitemap":3281,"stem":3282,"updatedAt":1837,"wordCount":3283,"__hash__":3284},"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":14,"value":1848,"toc":3249},[1849,1852,1859,1863,1866,1874,1877,1880,1884,1887,1895,1898,1904,1907,1913,1916,1922,1925,1931,1934,1940,1946,1950,1953,1959,1962,1968,1971,1977,1980,1986,1989,1995,2002,2007,2010,2014,2021,2024,2555,2558,2679,2689,2692,2715,2721,2728,2736,2740,2743,2935,2938,2942,2946,2949,2990,2997,3004,3008,3011,3018,3045,3052,3055,3059,3062,3065,3099,3102,3106,3109,3112,3114,3118,3121,3128,3131,3135,3138,3151,3154,3157,3189,3193,3196,3199,3202,3206,3215,3218,3222,3225,3228,3232,3238,3246],[17,1850,1851],{},"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.",[17,1853,1854,1855,1858],{},"In this article, you will understand the CPF algorithm and implement a ",[63,1856,1857],{},"CPF validation"," function in plain JavaScript, with no external library.",[24,1860,1862],{"id":1861},"what-the-check-digits-are","What the check digits are",[17,1864,1865],{},"A CPF has 11 digits:",[312,1867,1868,1871],{},[315,1869,1870],{},"The first 9 are the base of the document.",[315,1872,1873],{},"The last 2 are the check digits.",[17,1875,1876],{},"These two digits are calculated from the previous ones. The idea is to catch common typing errors, like swapping or dropping a number.",[17,1878,1879],{},"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.",[844,1881,1883],{"id":1882},"first-check-digit","First check digit",[17,1885,1886],{},"Take the CPF without formatting:",[87,1888,1893],{"className":1889,"code":1891,"language":1892,"meta":92},[1890],"language-text","52998224725\n","text",[75,1894,1891],{"__ignoreMap":92},[17,1896,1897],{},"The first 9 digits are:",[87,1899,1902],{"className":1900,"code":1901,"language":1892,"meta":92},[1890],"5 2 9 9 8 2 2 4 7\n",[75,1903,1901],{"__ignoreMap":92},[17,1905,1906],{},"Multiply each number by the weights from 10 down to 2:",[87,1908,1911],{"className":1909,"code":1910,"language":1892,"meta":92},[1890],"5×10 + 2×9 + 9×8 + 9×7 + 8×6 + 2×5 + 2×4 + 4×3 + 7×2\n",[75,1912,1910],{"__ignoreMap":92},[17,1914,1915],{},"The result is:",[87,1917,1920],{"className":1918,"code":1919,"language":1892,"meta":92},[1890],"50 + 18 + 72 + 63 + 48 + 10 + 8 + 12 + 14 = 295\n",[75,1921,1919],{"__ignoreMap":92},[17,1923,1924],{},"Now calculate the remainder of the division by 11:",[87,1926,1929],{"className":1927,"code":1928,"language":1892,"meta":92},[1890],"295 % 11 = 9\n",[75,1930,1928],{"__ignoreMap":92},[17,1932,1933],{},"Since the remainder is greater than 1, we do:",[87,1935,1938],{"className":1936,"code":1937,"language":1892,"meta":92},[1890],"11 - 9 = 2\n",[75,1939,1937],{"__ignoreMap":92},[17,1941,1942,1943,366],{},"So the first check digit is ",[75,1944,1945],{},"2",[844,1947,1949],{"id":1948},"second-check-digit","Second check digit",[17,1951,1952],{},"Now use the first 10 digits, including the check digit you just calculated:",[87,1954,1957],{"className":1955,"code":1956,"language":1892,"meta":92},[1890],"5 2 9 9 8 2 2 4 7 2\n",[75,1958,1956],{"__ignoreMap":92},[17,1960,1961],{},"Apply the weights from 11 down to 2:",[87,1963,1966],{"className":1964,"code":1965,"language":1892,"meta":92},[1890],"5×11 + 2×10 + 9×9 + 9×8 + 8×7 + 2×6 + 2×5 + 4×4 + 7×3 + 2×2\n",[75,1967,1965],{"__ignoreMap":92},[17,1969,1970],{},"The sum is:",[87,1972,1975],{"className":1973,"code":1974,"language":1892,"meta":92},[1890],"55 + 20 + 81 + 72 + 56 + 12 + 10 + 16 + 21 + 4 = 347\n",[75,1976,1974],{"__ignoreMap":92},[17,1978,1979],{},"The remainder of the division by 11 is:",[87,1981,1984],{"className":1982,"code":1983,"language":1892,"meta":92},[1890],"347 % 11 = 6\n",[75,1985,1983],{"__ignoreMap":92},[17,1987,1988],{},"Since the remainder is greater than 1:",[87,1990,1993],{"className":1991,"code":1992,"language":1892,"meta":92},[1890],"11 - 6 = 5\n",[75,1994,1992],{"__ignoreMap":92},[17,1996,1997,1998,2001],{},"The second check digit is ",[75,1999,2000],{},"5",". So the full CPF is:",[87,2003,2005],{"className":2004,"code":1891,"language":1892,"meta":92},[1890],[75,2006,1891],{"__ignoreMap":92},[17,2008,2009],{},"That is the CPF algorithm: calculate the first digit, add it to the sequence and calculate the second.",[24,2011,2013],{"id":2012},"implementation-in-plain-javascript","Implementation in plain JavaScript",[17,2015,2016,2017,2020],{},"The function needs to handle formatted input, like ",[75,2018,2019],{},"529.982.247-25",", and also a string of digits with no punctuation.",[17,2022,2023],{},"One possible implementation is this:",[87,2025,2028],{"className":2026,"code":2027,"language":468,"meta":92,"style":92},"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",[75,2029,2030,2046,2051,2089,2093,2098,2114,2125,2130,2134,2139,2172,2181,2186,2191,2197,2213,2218,2251,2280,2285,2290,2309,2342,2347,2353,2374,2383,2388,2393,2399,2411,2416,2444,2466,2471,2476,2492,2520,2525,2531,2550],{"__ignoreMap":92},[96,2031,2032,2035,2038,2040,2043],{"class":98,"line":99},[96,2033,2034],{"class":102},"function",[96,2036,2037],{"class":968}," validateCPF",[96,2039,972],{"class":110},[96,2041,2042],{"class":1592},"cpf",[96,2044,2045],{"class":110},") {\n",[96,2047,2048],{"class":98,"line":454},[96,2049,2050],{"class":114},"  \u002F\u002F Make sure the input is treated as text\n",[96,2052,2053,2056,2058,2061,2064,2067,2069,2073,2076,2078,2081,2083,2086],{"class":98,"line":951},[96,2054,2055],{"class":110},"  cpf ",[96,2057,241],{"class":102},[96,2059,2060],{"class":968}," String",[96,2062,2063],{"class":110},"(cpf).",[96,2065,2066],{"class":968},"replace",[96,2068,972],{"class":110},[96,2070,2072],{"class":2071},"s-doY","\u002F",[96,2074,2075],{"class":106},"\\D",[96,2077,2072],{"class":2071},[96,2079,2080],{"class":102},"g",[96,2082,864],{"class":110},[96,2084,2085],{"class":919},"\"\"",[96,2087,2088],{"class":110},");\n",[96,2090,2091],{"class":98,"line":957},[96,2092,954],{"emptyLinePlaceholder":473},[96,2094,2095],{"class":98,"line":981},[96,2096,2097],{"class":114},"  \u002F\u002F A valid CPF must have exactly 11 digits\n",[96,2099,2100,2103,2106,2109,2112],{"class":98,"line":991},[96,2101,2102],{"class":102},"  if",[96,2104,2105],{"class":110}," (cpf.length ",[96,2107,2108],{"class":102},"!==",[96,2110,2111],{"class":106}," 11",[96,2113,2045],{"class":110},[96,2115,2116,2119,2122],{"class":98,"line":996},[96,2117,2118],{"class":102},"    return",[96,2120,2121],{"class":106}," false",[96,2123,2124],{"class":110},";\n",[96,2126,2127],{"class":98,"line":1006},[96,2128,2129],{"class":110},"  }\n",[96,2131,2132],{"class":98,"line":1029},[96,2133,954],{"emptyLinePlaceholder":473},[96,2135,2136],{"class":98,"line":1035},[96,2137,2138],{"class":114},"  \u002F\u002F Reject CPFs made of the same digit\n",[96,2140,2141,2143,2145,2147,2150,2153,2156,2159,2162,2164,2166,2169],{"class":98,"line":1045},[96,2142,2102],{"class":102},[96,2144,1589],{"class":110},[96,2146,2072],{"class":2071},[96,2148,2149],{"class":102},"^",[96,2151,972],{"class":2152},"smvxG",[96,2154,2155],{"class":106},"\\d",[96,2157,2158],{"class":2152},")",[96,2160,2161],{"class":102},"\\1{10}$",[96,2163,2072],{"class":2071},[96,2165,366],{"class":110},[96,2167,2168],{"class":968},"test",[96,2170,2171],{"class":110},"(cpf)) {\n",[96,2173,2175,2177,2179],{"class":98,"line":2174},12,[96,2176,2118],{"class":102},[96,2178,2121],{"class":106},[96,2180,2124],{"class":110},[96,2182,2184],{"class":98,"line":2183},13,[96,2185,2129],{"class":110},[96,2187,2189],{"class":98,"line":2188},14,[96,2190,954],{"emptyLinePlaceholder":473},[96,2192,2194],{"class":98,"line":2193},15,[96,2195,2196],{"class":114},"  \u002F\u002F Calculate the first check digit\n",[96,2198,2200,2203,2206,2208,2211],{"class":98,"line":2199},16,[96,2201,2202],{"class":102},"  let",[96,2204,2205],{"class":110}," sum ",[96,2207,241],{"class":102},[96,2209,2210],{"class":106}," 0",[96,2212,2124],{"class":110},[96,2214,2216],{"class":98,"line":2215},17,[96,2217,954],{"emptyLinePlaceholder":473},[96,2219,2221,2224,2226,2228,2231,2233,2235,2238,2240,2243,2246,2249],{"class":98,"line":2220},18,[96,2222,2223],{"class":102},"  for",[96,2225,1589],{"class":110},[96,2227,235],{"class":102},[96,2229,2230],{"class":110}," i ",[96,2232,241],{"class":102},[96,2234,2210],{"class":106},[96,2236,2237],{"class":110},"; i ",[96,2239,901],{"class":102},[96,2241,2242],{"class":106}," 9",[96,2244,2245],{"class":110},"; i",[96,2247,2248],{"class":102},"++",[96,2250,2045],{"class":110},[96,2252,2254,2257,2260,2263,2266,2269,2271,2274,2277],{"class":98,"line":2253},19,[96,2255,2256],{"class":110},"    sum ",[96,2258,2259],{"class":102},"+=",[96,2261,2262],{"class":968}," Number",[96,2264,2265],{"class":110},"(cpf[i]) ",[96,2267,2268],{"class":102},"*",[96,2270,1589],{"class":110},[96,2272,2273],{"class":106},"10",[96,2275,2276],{"class":102}," -",[96,2278,2279],{"class":110}," i);\n",[96,2281,2283],{"class":98,"line":2282},20,[96,2284,2129],{"class":110},[96,2286,2288],{"class":98,"line":2287},21,[96,2289,954],{"emptyLinePlaceholder":473},[96,2291,2293,2295,2298,2300,2302,2305,2307],{"class":98,"line":2292},22,[96,2294,2202],{"class":102},[96,2296,2297],{"class":110}," remainder ",[96,2299,241],{"class":102},[96,2301,2205],{"class":110},[96,2303,2304],{"class":102},"%",[96,2306,2111],{"class":106},[96,2308,2124],{"class":110},[96,2310,2312,2315,2318,2320,2322,2324,2327,2330,2332,2335,2337,2339],{"class":98,"line":2311},23,[96,2313,2314],{"class":102},"  const",[96,2316,2317],{"class":110}," firstDigit ",[96,2319,241],{"class":102},[96,2321,2297],{"class":110},[96,2323,901],{"class":102},[96,2325,2326],{"class":106}," 2",[96,2328,2329],{"class":102}," ?",[96,2331,2210],{"class":106},[96,2333,2334],{"class":102}," :",[96,2336,2111],{"class":106},[96,2338,2276],{"class":102},[96,2340,2341],{"class":110}," remainder;\n",[96,2343,2345],{"class":98,"line":2344},24,[96,2346,954],{"emptyLinePlaceholder":473},[96,2348,2350],{"class":98,"line":2349},25,[96,2351,2352],{"class":114},"  \u002F\u002F The calculated first digit must match the one provided\n",[96,2354,2356,2358,2361,2363,2365,2368,2371],{"class":98,"line":2355},26,[96,2357,2102],{"class":102},[96,2359,2360],{"class":110}," (firstDigit ",[96,2362,2108],{"class":102},[96,2364,2262],{"class":968},[96,2366,2367],{"class":110},"(cpf[",[96,2369,2370],{"class":106},"9",[96,2372,2373],{"class":110},"])) {\n",[96,2375,2377,2379,2381],{"class":98,"line":2376},27,[96,2378,2118],{"class":102},[96,2380,2121],{"class":106},[96,2382,2124],{"class":110},[96,2384,2386],{"class":98,"line":2385},28,[96,2387,2129],{"class":110},[96,2389,2391],{"class":98,"line":2390},29,[96,2392,954],{"emptyLinePlaceholder":473},[96,2394,2396],{"class":98,"line":2395},30,[96,2397,2398],{"class":114},"  \u002F\u002F Calculate the second check digit\n",[96,2400,2402,2405,2407,2409],{"class":98,"line":2401},31,[96,2403,2404],{"class":110},"  sum ",[96,2406,241],{"class":102},[96,2408,2210],{"class":106},[96,2410,2124],{"class":110},[96,2412,2414],{"class":98,"line":2413},32,[96,2415,954],{"emptyLinePlaceholder":473},[96,2417,2419,2421,2423,2425,2427,2429,2431,2433,2435,2438,2440,2442],{"class":98,"line":2418},33,[96,2420,2223],{"class":102},[96,2422,1589],{"class":110},[96,2424,235],{"class":102},[96,2426,2230],{"class":110},[96,2428,241],{"class":102},[96,2430,2210],{"class":106},[96,2432,2237],{"class":110},[96,2434,901],{"class":102},[96,2436,2437],{"class":106}," 10",[96,2439,2245],{"class":110},[96,2441,2248],{"class":102},[96,2443,2045],{"class":110},[96,2445,2447,2449,2451,2453,2455,2457,2459,2462,2464],{"class":98,"line":2446},34,[96,2448,2256],{"class":110},[96,2450,2259],{"class":102},[96,2452,2262],{"class":968},[96,2454,2265],{"class":110},[96,2456,2268],{"class":102},[96,2458,1589],{"class":110},[96,2460,2461],{"class":106},"11",[96,2463,2276],{"class":102},[96,2465,2279],{"class":110},[96,2467,2469],{"class":98,"line":2468},35,[96,2470,2129],{"class":110},[96,2472,2474],{"class":98,"line":2473},36,[96,2475,954],{"emptyLinePlaceholder":473},[96,2477,2479,2482,2484,2486,2488,2490],{"class":98,"line":2478},37,[96,2480,2481],{"class":110},"  remainder ",[96,2483,241],{"class":102},[96,2485,2205],{"class":110},[96,2487,2304],{"class":102},[96,2489,2111],{"class":106},[96,2491,2124],{"class":110},[96,2493,2495,2497,2500,2502,2504,2506,2508,2510,2512,2514,2516,2518],{"class":98,"line":2494},38,[96,2496,2314],{"class":102},[96,2498,2499],{"class":110}," secondDigit ",[96,2501,241],{"class":102},[96,2503,2297],{"class":110},[96,2505,901],{"class":102},[96,2507,2326],{"class":106},[96,2509,2329],{"class":102},[96,2511,2210],{"class":106},[96,2513,2334],{"class":102},[96,2515,2111],{"class":106},[96,2517,2276],{"class":102},[96,2519,2341],{"class":110},[96,2521,2523],{"class":98,"line":2522},39,[96,2524,954],{"emptyLinePlaceholder":473},[96,2526,2528],{"class":98,"line":2527},40,[96,2529,2530],{"class":114},"  \u002F\u002F The calculated second digit must also match the one provided\n",[96,2532,2534,2536,2538,2541,2543,2545,2547],{"class":98,"line":2533},41,[96,2535,1641],{"class":102},[96,2537,2499],{"class":110},[96,2539,2540],{"class":102},"===",[96,2542,2262],{"class":968},[96,2544,2367],{"class":110},[96,2546,2273],{"class":106},[96,2548,2549],{"class":110},"]);\n",[96,2551,2553],{"class":98,"line":2552},42,[96,2554,1668],{"class":110},[17,2556,2557],{},"Usage examples:",[87,2559,2561],{"className":2026,"code":2560,"language":468,"meta":92,"style":92},"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",[75,2562,2563,2586,2591,2595,2616,2620,2624,2645,2650,2654,2675],{"__ignoreMap":92},[96,2564,2565,2568,2570,2572,2575,2577,2579,2581,2583],{"class":98,"line":99},[96,2566,2567],{"class":110},"console.",[96,2569,1652],{"class":968},[96,2571,972],{"class":110},[96,2573,2574],{"class":968},"validateCPF",[96,2576,972],{"class":110},[96,2578,920],{"class":919},[96,2580,2019],{"class":923},[96,2582,920],{"class":919},[96,2584,2585],{"class":110},"));\n",[96,2587,2588],{"class":98,"line":454},[96,2589,2590],{"class":114},"\u002F\u002F true\n",[96,2592,2593],{"class":98,"line":951},[96,2594,954],{"emptyLinePlaceholder":473},[96,2596,2597,2599,2601,2603,2605,2607,2609,2612,2614],{"class":98,"line":957},[96,2598,2567],{"class":110},[96,2600,1652],{"class":968},[96,2602,972],{"class":110},[96,2604,2574],{"class":968},[96,2606,972],{"class":110},[96,2608,920],{"class":919},[96,2610,2611],{"class":923},"52998224725",[96,2613,920],{"class":919},[96,2615,2585],{"class":110},[96,2617,2618],{"class":98,"line":981},[96,2619,2590],{"class":114},[96,2621,2622],{"class":98,"line":991},[96,2623,954],{"emptyLinePlaceholder":473},[96,2625,2626,2628,2630,2632,2634,2636,2638,2641,2643],{"class":98,"line":996},[96,2627,2567],{"class":110},[96,2629,1652],{"class":968},[96,2631,972],{"class":110},[96,2633,2574],{"class":968},[96,2635,972],{"class":110},[96,2637,920],{"class":919},[96,2639,2640],{"class":923},"529.982.247-26",[96,2642,920],{"class":919},[96,2644,2585],{"class":110},[96,2646,2647],{"class":98,"line":1006},[96,2648,2649],{"class":114},"\u002F\u002F false\n",[96,2651,2652],{"class":98,"line":1029},[96,2653,954],{"emptyLinePlaceholder":473},[96,2655,2656,2658,2660,2662,2664,2666,2668,2671,2673],{"class":98,"line":1035},[96,2657,2567],{"class":110},[96,2659,1652],{"class":968},[96,2661,972],{"class":110},[96,2663,2574],{"class":968},[96,2665,972],{"class":110},[96,2667,920],{"class":919},[96,2669,2670],{"class":923},"111.111.111-11",[96,2672,920],{"class":919},[96,2674,2585],{"class":110},[96,2676,2677],{"class":98,"line":1045},[96,2678,2649],{"class":114},[17,2680,1541,2681,2684,2685,156,2687,366],{},[75,2682,2683],{},"replace(\u002F\\D\u002Fg, '')"," call removes everything that is not a digit. That lets the function accept both ",[75,2686,2019],{},[75,2688,2611],{},[17,2690,2691],{},"This is the regular expression that catches repeated digits:",[87,2693,2695],{"className":2026,"code":2694,"language":468,"meta":92,"style":92},"\u002F^(\\d)\\1{10}$\u002F;\n",[75,2696,2697],{"__ignoreMap":92},[96,2698,2699,2701,2703,2705,2707,2709,2711,2713],{"class":98,"line":99},[96,2700,2072],{"class":2071},[96,2702,2149],{"class":102},[96,2704,972],{"class":2152},[96,2706,2155],{"class":106},[96,2708,2158],{"class":2152},[96,2710,2161],{"class":102},[96,2712,2072],{"class":2071},[96,2714,2124],{"class":110},[17,2716,2717,2718,2720],{},"It matches an 11-character sequence made of the same digit. That keeps values like ",[75,2719,2670],{}," from being accepted.",[17,2722,2723,2724,2727],{},"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 ",[75,2725,2726],{},"11111111111"," as valid.",[17,2729,2730,2731,366],{},"If you want to test inputs quickly without building a form, use ",[32,2732,2735],{"href":2733,"rel":2734},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcpf-validator",[36],"our free online CPF validator",[844,2737,2739],{"id":2738},"validation-in-the-form","Validation in the form",[17,2741,2742],{},"You can call the function on the form's submit event:",[87,2744,2746],{"className":2026,"code":2745,"language":468,"meta":92,"style":92},"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",[75,2747,2748,2774,2798,2822,2826,2854,2865,2869,2883,2900,2906,2910,2914,2930],{"__ignoreMap":92},[96,2749,2750,2752,2755,2757,2760,2763,2765,2767,2770,2772],{"class":98,"line":99},[96,2751,960],{"class":102},[96,2753,2754],{"class":110}," form ",[96,2756,241],{"class":102},[96,2758,2759],{"class":110}," document.",[96,2761,2762],{"class":968},"querySelector",[96,2764,972],{"class":110},[96,2766,920],{"class":919},[96,2768,2769],{"class":923},"#form",[96,2771,920],{"class":919},[96,2773,2088],{"class":110},[96,2775,2776,2778,2781,2783,2785,2787,2789,2791,2794,2796],{"class":98,"line":454},[96,2777,960],{"class":102},[96,2779,2780],{"class":110}," cpfField ",[96,2782,241],{"class":102},[96,2784,2759],{"class":110},[96,2786,2762],{"class":968},[96,2788,972],{"class":110},[96,2790,920],{"class":919},[96,2792,2793],{"class":923},"#cpf",[96,2795,920],{"class":919},[96,2797,2088],{"class":110},[96,2799,2800,2802,2805,2807,2809,2811,2813,2815,2818,2820],{"class":98,"line":951},[96,2801,960],{"class":102},[96,2803,2804],{"class":110}," message ",[96,2806,241],{"class":102},[96,2808,2759],{"class":110},[96,2810,2762],{"class":968},[96,2812,972],{"class":110},[96,2814,920],{"class":919},[96,2816,2817],{"class":923},"#message",[96,2819,920],{"class":919},[96,2821,2088],{"class":110},[96,2823,2824],{"class":98,"line":957},[96,2825,954],{"emptyLinePlaceholder":473},[96,2827,2828,2831,2834,2836,2838,2841,2843,2845,2847,2849,2852],{"class":98,"line":981},[96,2829,2830],{"class":110},"form.",[96,2832,2833],{"class":968},"addEventListener",[96,2835,972],{"class":110},[96,2837,920],{"class":919},[96,2839,2840],{"class":923},"submit",[96,2842,920],{"class":919},[96,2844,864],{"class":110},[96,2846,2034],{"class":102},[96,2848,1589],{"class":110},[96,2850,2851],{"class":1592},"event",[96,2853,2045],{"class":110},[96,2855,2856,2859,2862],{"class":98,"line":991},[96,2857,2858],{"class":110},"  event.",[96,2860,2861],{"class":968},"preventDefault",[96,2863,2864],{"class":110},"();\n",[96,2866,2867],{"class":98,"line":996},[96,2868,954],{"emptyLinePlaceholder":473},[96,2870,2871,2873,2875,2878,2880],{"class":98,"line":1006},[96,2872,2102],{"class":102},[96,2874,1589],{"class":110},[96,2876,2877],{"class":102},"!",[96,2879,2574],{"class":968},[96,2881,2882],{"class":110},"(cpfField.value)) {\n",[96,2884,2885,2888,2890,2893,2896,2898],{"class":98,"line":1029},[96,2886,2887],{"class":110},"    message.textContent ",[96,2889,241],{"class":102},[96,2891,2892],{"class":919}," \"",[96,2894,2895],{"class":923},"Enter a valid CPF.",[96,2897,920],{"class":919},[96,2899,2124],{"class":110},[96,2901,2902,2904],{"class":98,"line":1035},[96,2903,2118],{"class":102},[96,2905,2124],{"class":110},[96,2907,2908],{"class":98,"line":1045},[96,2909,2129],{"class":110},[96,2911,2912],{"class":98,"line":2174},[96,2913,954],{"emptyLinePlaceholder":473},[96,2915,2916,2919,2921,2923,2926,2928],{"class":98,"line":2183},[96,2917,2918],{"class":110},"  message.textContent ",[96,2920,241],{"class":102},[96,2922,2892],{"class":919},[96,2924,2925],{"class":923},"Valid CPF.",[96,2927,920],{"class":919},[96,2929,2124],{"class":110},[96,2931,2932],{"class":98,"line":2188},[96,2933,2934],{"class":110},"});\n",[17,2936,2937],{},"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.",[24,2939,2941],{"id":2940},"common-mistakes","Common mistakes",[844,2943,2945],{"id":2944},"validating-a-cpf-with-a-format-regex-only","Validating a CPF with a format regex only",[17,2947,2948],{},"An expression like this one:",[87,2950,2952],{"className":2026,"code":2951,"language":468,"meta":92,"style":92},"\u002F^\\d{3}\\.\\d{3}\\.\\d{3}-\\d{2}$\u002F;\n",[75,2953,2954],{"__ignoreMap":92},[96,2955,2956,2958,2960,2962,2965,2968,2970,2972,2974,2976,2978,2981,2983,2986,2988],{"class":98,"line":99},[96,2957,2072],{"class":2071},[96,2959,2149],{"class":102},[96,2961,2155],{"class":106},[96,2963,2964],{"class":102},"{3}",[96,2966,2967],{"class":923},"\\.",[96,2969,2155],{"class":106},[96,2971,2964],{"class":102},[96,2973,2967],{"class":923},[96,2975,2155],{"class":106},[96,2977,2964],{"class":102},[96,2979,2980],{"class":923},"-",[96,2982,2155],{"class":106},[96,2984,2985],{"class":102},"{2}$",[96,2987,2072],{"class":2071},[96,2989,2124],{"class":110},[17,2991,2992,2993,2996],{},"only checks whether the text has the ",[75,2994,2995],{},"000.000.000-00"," format. It does not calculate the check digits.",[17,2998,2999,3000,3003],{},"For example, ",[75,3001,3002],{},"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.",[844,3005,3007],{"id":3006},"forgetting-leading-zeros","Forgetting leading zeros",[17,3009,3010],{},"A CPF is an identifier, not a number for regular math. So treat it as a string.",[17,3012,3013,3014,3017],{},"If you convert a CPF to ",[75,3015,3016],{},"Number",", a value that starts with zero can lose that zero:",[87,3019,3021],{"className":2026,"code":3020,"language":468,"meta":92,"style":92},"const cpf = Number(\"012.345.678-90\");\n",[75,3022,3023],{"__ignoreMap":92},[96,3024,3025,3027,3030,3032,3034,3036,3038,3041,3043],{"class":98,"line":99},[96,3026,960],{"class":102},[96,3028,3029],{"class":110}," cpf ",[96,3031,241],{"class":102},[96,3033,2262],{"class":968},[96,3035,972],{"class":110},[96,3037,920],{"class":919},[96,3039,3040],{"class":923},"012.345.678-90",[96,3042,920],{"class":919},[96,3044,2088],{"class":110},[17,3046,3047,3048,3051],{},"Also, very large numbers should not be the main representation of documents. The function in this article converts the input to text with ",[75,3049,3050],{},"String(cpf)"," and removes the formatting before calculating.",[17,3053,3054],{},"In practice, keep the value from the HTML field as a string from the start.",[844,3056,3058],{"id":3057},"not-normalizing-the-input","Not normalizing the input",[17,3060,3061],{},"If the code expects exactly 11 digits, it may reject a formatted CPF before it even runs the calculation.",[17,3063,3064],{},"This step fixes the problem:",[87,3066,3068],{"className":2026,"code":3067,"language":468,"meta":92,"style":92},"cpf = String(cpf).replace(\u002F\\D\u002Fg, \"\");\n",[75,3069,3070],{"__ignoreMap":92},[96,3071,3072,3075,3077,3079,3081,3083,3085,3087,3089,3091,3093,3095,3097],{"class":98,"line":99},[96,3073,3074],{"class":110},"cpf ",[96,3076,241],{"class":102},[96,3078,2060],{"class":968},[96,3080,2063],{"class":110},[96,3082,2066],{"class":968},[96,3084,972],{"class":110},[96,3086,2072],{"class":2071},[96,3088,2075],{"class":106},[96,3090,2072],{"class":2071},[96,3092,2080],{"class":102},[96,3094,864],{"class":110},[96,3096,2085],{"class":919},[96,3098,2088],{"class":110},[17,3100,3101],{},"It removes dots, dashes, spaces and other non-numeric characters. After that, the function checks whether exactly 11 digits are left.",[844,3103,3105],{"id":3104},"validating-only-in-the-browser","Validating only in the browser",[17,3107,3108],{},"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.",[17,3110,3111],{},"The same rule should exist on the back-end, preferably in a single function covered by tests.",[24,3113,1728],{"id":1727},[844,3115,3117],{"id":3116},"how-do-i-know-if-a-cpf-is-valid","How do I know if a CPF is valid?",[17,3119,3120],{},"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.",[17,3122,3123,3124,3127],{},"The CPF is only mathematically valid if the two calculated digits match the last two digits provided. The ",[75,3125,3126],{},"validateCPF(cpf)"," function in this article runs all of these steps.",[17,3129,3130],{},"Passing the math check says nothing about whether the document is active, in good standing or tied to a specific person.",[844,3132,3134],{"id":3133},"how-do-i-calculate-the-cpf-check-digit","How do I calculate the CPF check digit?",[17,3136,3137],{},"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.",[312,3139,3140,3145],{},[315,3141,3142,3143,366],{},"If the remainder is 0 or 1, the digit is ",[75,3144,975],{},[315,3146,3147,3148,366],{},"Otherwise, the digit is ",[75,3149,3150],{},"11 - remainder",[17,3152,3153],{},"For the second digit, do the same with the first 10 digits and the weights from 11 down to 2.",[17,3155,3156],{},"In JavaScript, the core of the calculation can be written like this:",[87,3158,3160],{"className":2026,"code":3159,"language":468,"meta":92,"style":92},"const digit = remainder \u003C 2 ? 0 : 11 - remainder;\n",[75,3161,3162],{"__ignoreMap":92},[96,3163,3164,3166,3169,3171,3173,3175,3177,3179,3181,3183,3185,3187],{"class":98,"line":99},[96,3165,960],{"class":102},[96,3167,3168],{"class":110}," digit ",[96,3170,241],{"class":102},[96,3172,2297],{"class":110},[96,3174,901],{"class":102},[96,3176,2326],{"class":106},[96,3178,2329],{"class":102},[96,3180,2210],{"class":106},[96,3182,2334],{"class":102},[96,3184,2111],{"class":106},[96,3186,2276],{"class":102},[96,3188,2341],{"class":110},[844,3190,3192],{"id":3191},"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?",[17,3194,3195],{},"No. The math check runs locally: the system calculates the digits and compares the result with the CPF provided.",[17,3197,3198],{},"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.",[17,3200,3201],{},"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.",[844,3203,3205],{"id":3204},"can-i-generate-a-valid-cpf-for-testing","Can I generate a valid CPF for testing?",[17,3207,3208,3209,3214],{},"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, ",[32,3210,3213],{"href":3211,"rel":3212},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcpf-generator",[36],"our CPF generator"," handles that in seconds.",[17,3216,3217],{},"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.",[844,3219,3221],{"id":3220},"does-the-function-check-whether-the-cpf-belongs-to-the-user","Does the function check whether the CPF belongs to the user?",[17,3223,3224],{},"No. It only checks the normalized format, the number of digits, the check digit rule and the rejection of repeated sequences.",[17,3226,3227],{},"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.",[24,3229,3231],{"id":3230},"next-steps","Next steps",[17,3233,3234,3235,3237],{},"You now have the CPF algorithm and a ",[75,3236,3126],{}," 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.",[17,3239,3240,3241,3245],{},"If you want to check values without writing code, use ",[32,3242,3244],{"href":2733,"rel":3243},[36],"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.",[450,3247,3248],{},"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":92,"searchDepth":454,"depth":454,"links":3250},[3251,3255,3258,3264,3271],{"id":1861,"depth":454,"text":1862,"children":3252},[3253,3254],{"id":1882,"depth":951,"text":1883},{"id":1948,"depth":951,"text":1949},{"id":2012,"depth":454,"text":2013,"children":3256},[3257],{"id":2738,"depth":951,"text":2739},{"id":2940,"depth":454,"text":2941,"children":3259},[3260,3261,3262,3263],{"id":2944,"depth":951,"text":2945},{"id":3006,"depth":951,"text":3007},{"id":3057,"depth":951,"text":3058},{"id":3104,"depth":951,"text":3105},{"id":1727,"depth":454,"text":1728,"children":3265},[3266,3267,3268,3269,3270],{"id":3116,"depth":951,"text":3117},{"id":3133,"depth":951,"text":3134},{"id":3191,"depth":951,"text":3192},{"id":3204,"depth":951,"text":3205},{"id":3220,"depth":951,"text":3221},{"id":3230,"depth":454,"text":3231},[468,3273],"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":1845,"description":3274},{"loc":3277,"lastmod":1837},"en\u002Fblog\u002Fhow-to-validate-cpf-with-javascript",1253,"rl6GsOytKht8bBLg9yWipV1zFDvkHy7ruCKxlllBid0",{"id":3286,"title":3287,"articleCover":3288,"articleCoverEmbed":486,"articleCoverEmbedUploadDate":486,"articleCoverHeight":10,"articleCoverWidth":11,"authorId":12,"body":3289,"categoriesIds":4030,"description":4031,"enAlternateSlug":4032,"extension":471,"meta":4033,"navigation":473,"path":4034,"ptBrAlternateSlug":4035,"publishedAt":3279,"seo":4036,"sitemap":4037,"stem":4038,"updatedAt":3279,"wordCount":4039,"__hash__":4040},"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":14,"value":3290,"toc":4007},[3291,3294,3297,3301,3308,3311,3314,3318,3328,3331,3364,3368,3383,3409,3415,3418,3420,3423,3819,3836,3840,3851,3868,3874,3877,3879,3882,3886,3893,3897,3910,3914,3917,3921,3928,3937,3939,3943,3954,3958,3961,3965,3968,3972,3975,3979,3982,3986,3989,3993,3996,4004],[17,3292,3293],{},"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.",[17,3295,3296],{},"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.",[24,3298,3300],{"id":3299},"what-the-luhn-algorithm-is","What the Luhn algorithm is",[17,3302,3303,3304,3307],{},"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 ",[63,3305,3306],{},"check digit"," (the last digit of the number) that catches typing errors, like swapping two digits or typing a wrong number.",[17,3309,3310],{},"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.",[17,3312,3313],{},"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.",[24,3315,3317],{"id":3316},"the-algorithm-step-by-step-with-a-numeric-example","The algorithm step by step with a numeric example",[17,3319,3320,3321,3324,3325,366],{},"Let's validate the Visa test number that many payment platforms use in sandbox: ",[63,3322,3323],{},"4242 4242 4242 4242",". First, we remove spaces and dashes, which leaves ",[75,3326,3327],{},"4242424242424242",[17,3329,3330],{},"Now we apply the Luhn algorithm:",[3332,3333,3334,3340,3346,3352,3358],"ol",{},[315,3335,3336,3339],{},[63,3337,3338],{},"Start from the rightmost digit"," (the last 2) and move left. This first digit is never doubled.",[315,3341,3342,3345],{},[63,3343,3344],{},"Double the value of every second digit"," from there, that is, skip one, double the next, skip one, double the next, and so on.",[315,3347,3348,3351],{},[63,3349,3350],{},"If doubling gives a two-digit result",", add the two digits together (or subtract 9, which is equivalent).",[315,3353,3354,3357],{},[63,3355,3356],{},"Add up all the digits"," (the doubled\u002Fadjusted ones and the ones left unchanged).",[315,3359,3360,3363],{},[63,3361,3362],{},"If the total is divisible by 10",", the number is valid according to the Luhn algorithm.",[844,3365,3367],{"id":3366},"hands-on-example-with-4242424242424242","Hands-on example with 4242424242424242",[17,3369,3370,3371,156,3374,3376,3377,3379,3380,3382],{},"The number has 16 digits, alternating ",[75,3372,3373],{},"4",[75,3375,1945],{},". Starting from the rightmost digit (which is not doubled) and moving left, the digits in the \"double\" position are always the ",[75,3378,3373],{},"s, and the ones in the \"don't double\" position are always the ",[75,3381,1945],{},"s. That happens because the whole number follows this alternating pattern.",[312,3384,3385,3394],{},[315,3386,3387,3390,3391,3393],{},[63,3388,3389],{},"Digits that are not doubled"," (8 digits, all ",[75,3392,1945],{},"): 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 = 16",[315,3395,3396,3390,3399,3401,3402,3404,3405,3408],{},[63,3397,3398],{},"Doubled digits",[75,3400,3373],{},"): each doubled ",[75,3403,3373],{}," becomes ",[75,3406,3407],{},"8"," (4 × 2 = 8, not above 9, no adjustment needed). 8 + 8 + 8 + 8 + 8 + 8 + 8 + 8 = 64",[17,3410,3411,3412,366],{},"Adding the two groups: 16 + 64 = ",[63,3413,3414],{},"80",[17,3416,3417],{},"Since 80 is divisible by 10 (remainder 0), this number passes the Luhn check.",[24,3419,2013],{"id":2012},[17,3421,3422],{},"Below is a complete function that removes spaces, dashes and other non-numeric characters, then applies the Luhn algorithm:",[87,3424,3426],{"className":2026,"code":3425,"language":468,"meta":92,"style":92},"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",[75,3427,3428,3442,3447,3477,3481,3486,3512,3520,3524,3528,3540,3556,3560,3565,3598,3618,3622,3630,3642,3647,3661,3673,3678,3683,3687,3696,3712,3716,3720,3737,3741,3745,3750,3774,3796],{"__ignoreMap":92},[96,3429,3430,3432,3435,3437,3440],{"class":98,"line":99},[96,3431,2034],{"class":102},[96,3433,3434],{"class":968}," validateCreditCard",[96,3436,972],{"class":110},[96,3438,3439],{"class":1592},"number",[96,3441,2045],{"class":110},[96,3443,3444],{"class":98,"line":454},[96,3445,3446],{"class":114},"  \u002F\u002F Remove everything that is not a digit\n",[96,3448,3449,3451,3454,3456,3459,3461,3463,3465,3467,3469,3471,3473,3475],{"class":98,"line":951},[96,3450,2314],{"class":102},[96,3452,3453],{"class":110}," digits ",[96,3455,241],{"class":102},[96,3457,3458],{"class":110}," number.",[96,3460,2066],{"class":968},[96,3462,972],{"class":110},[96,3464,2072],{"class":2071},[96,3466,2075],{"class":106},[96,3468,2072],{"class":2071},[96,3470,2080],{"class":102},[96,3472,864],{"class":110},[96,3474,2085],{"class":919},[96,3476,2088],{"class":110},[96,3478,3479],{"class":98,"line":957},[96,3480,954],{"emptyLinePlaceholder":473},[96,3482,3483],{"class":98,"line":981},[96,3484,3485],{"class":114},"  \u002F\u002F Cards have between 13 and 19 digits (most have 16)\n",[96,3487,3488,3490,3493,3495,3498,3501,3504,3507,3510],{"class":98,"line":991},[96,3489,2102],{"class":102},[96,3491,3492],{"class":110}," (digits.length ",[96,3494,901],{"class":102},[96,3496,3497],{"class":106}," 13",[96,3499,3500],{"class":102}," ||",[96,3502,3503],{"class":110}," digits.length ",[96,3505,3506],{"class":102},">",[96,3508,3509],{"class":106}," 19",[96,3511,2045],{"class":110},[96,3513,3514,3516,3518],{"class":98,"line":996},[96,3515,2118],{"class":102},[96,3517,2121],{"class":106},[96,3519,2124],{"class":110},[96,3521,3522],{"class":98,"line":1006},[96,3523,2129],{"class":110},[96,3525,3526],{"class":98,"line":1029},[96,3527,954],{"emptyLinePlaceholder":473},[96,3529,3530,3532,3534,3536,3538],{"class":98,"line":1035},[96,3531,2202],{"class":102},[96,3533,2205],{"class":110},[96,3535,241],{"class":102},[96,3537,2210],{"class":106},[96,3539,2124],{"class":110},[96,3541,3542,3544,3547,3549,3551,3553],{"class":98,"line":1045},[96,3543,2202],{"class":102},[96,3545,3546],{"class":110}," shouldDouble ",[96,3548,241],{"class":102},[96,3550,2121],{"class":106},[96,3552,111],{"class":110},[96,3554,3555],{"class":114},"\u002F\u002F starts at the last digit (not doubled)\n",[96,3557,3558],{"class":98,"line":2174},[96,3559,954],{"emptyLinePlaceholder":473},[96,3561,3562],{"class":98,"line":2183},[96,3563,3564],{"class":114},"  \u002F\u002F Walk from right to left\n",[96,3566,3567,3569,3571,3573,3575,3577,3579,3581,3584,3586,3589,3591,3593,3596],{"class":98,"line":2188},[96,3568,2223],{"class":102},[96,3570,1589],{"class":110},[96,3572,235],{"class":102},[96,3574,2230],{"class":110},[96,3576,241],{"class":102},[96,3578,3503],{"class":110},[96,3580,2980],{"class":102},[96,3582,3583],{"class":106}," 1",[96,3585,2237],{"class":110},[96,3587,3588],{"class":102},">=",[96,3590,2210],{"class":106},[96,3592,2245],{"class":110},[96,3594,3595],{"class":102},"--",[96,3597,2045],{"class":110},[96,3599,3600,3603,3606,3608,3611,3614,3616],{"class":98,"line":2193},[96,3601,3602],{"class":102},"    let",[96,3604,3605],{"class":110}," d ",[96,3607,241],{"class":102},[96,3609,3610],{"class":968}," parseInt",[96,3612,3613],{"class":110},"(digits[i], ",[96,3615,2273],{"class":106},[96,3617,2088],{"class":110},[96,3619,3620],{"class":98,"line":2199},[96,3621,954],{"emptyLinePlaceholder":473},[96,3623,3624,3627],{"class":98,"line":2215},[96,3625,3626],{"class":102},"    if",[96,3628,3629],{"class":110}," (shouldDouble) {\n",[96,3631,3632,3635,3638,3640],{"class":98,"line":2220},[96,3633,3634],{"class":110},"      d ",[96,3636,3637],{"class":102},"*=",[96,3639,2326],{"class":106},[96,3641,2124],{"class":110},[96,3643,3644],{"class":98,"line":2253},[96,3645,3646],{"class":114},"      \u002F\u002F If the result has two digits, add them up (or subtract 9)\n",[96,3648,3649,3652,3655,3657,3659],{"class":98,"line":2282},[96,3650,3651],{"class":102},"      if",[96,3653,3654],{"class":110}," (d ",[96,3656,3506],{"class":102},[96,3658,2242],{"class":106},[96,3660,2045],{"class":110},[96,3662,3663,3666,3669,3671],{"class":98,"line":2287},[96,3664,3665],{"class":110},"        d ",[96,3667,3668],{"class":102},"-=",[96,3670,2242],{"class":106},[96,3672,2124],{"class":110},[96,3674,3675],{"class":98,"line":2292},[96,3676,3677],{"class":110},"      }\n",[96,3679,3680],{"class":98,"line":2311},[96,3681,3682],{"class":110},"    }\n",[96,3684,3685],{"class":98,"line":2344},[96,3686,954],{"emptyLinePlaceholder":473},[96,3688,3689,3691,3693],{"class":98,"line":2349},[96,3690,2256],{"class":110},[96,3692,2259],{"class":102},[96,3694,3695],{"class":110}," d;\n",[96,3697,3698,3701,3703,3706,3709],{"class":98,"line":2355},[96,3699,3700],{"class":110},"    shouldDouble ",[96,3702,241],{"class":102},[96,3704,3705],{"class":102}," !",[96,3707,3708],{"class":110},"shouldDouble; ",[96,3710,3711],{"class":114},"\u002F\u002F alternate for the next digit\n",[96,3713,3714],{"class":98,"line":2376},[96,3715,2129],{"class":110},[96,3717,3718],{"class":98,"line":2385},[96,3719,954],{"emptyLinePlaceholder":473},[96,3721,3722,3724,3726,3728,3730,3733,3735],{"class":98,"line":2390},[96,3723,1641],{"class":102},[96,3725,2205],{"class":110},[96,3727,2304],{"class":102},[96,3729,2437],{"class":106},[96,3731,3732],{"class":102}," ===",[96,3734,2210],{"class":106},[96,3736,2124],{"class":110},[96,3738,3739],{"class":98,"line":2395},[96,3740,1668],{"class":110},[96,3742,3743],{"class":98,"line":2401},[96,3744,954],{"emptyLinePlaceholder":473},[96,3746,3747],{"class":98,"line":2413},[96,3748,3749],{"class":114},"\u002F\u002F Testing with the Visa test number\n",[96,3751,3752,3754,3756,3758,3761,3763,3765,3767,3769,3772],{"class":98,"line":2418},[96,3753,2567],{"class":110},[96,3755,1652],{"class":968},[96,3757,972],{"class":110},[96,3759,3760],{"class":968},"validateCreditCard",[96,3762,972],{"class":110},[96,3764,920],{"class":919},[96,3766,3323],{"class":923},[96,3768,920],{"class":919},[96,3770,3771],{"class":110},")); ",[96,3773,2590],{"class":114},[96,3775,3776,3778,3780,3782,3784,3786,3788,3790,3792,3794],{"class":98,"line":2446},[96,3777,2567],{"class":110},[96,3779,1652],{"class":968},[96,3781,972],{"class":110},[96,3783,3760],{"class":968},[96,3785,972],{"class":110},[96,3787,920],{"class":919},[96,3789,3327],{"class":923},[96,3791,920],{"class":919},[96,3793,3771],{"class":110},[96,3795,2590],{"class":114},[96,3797,3798,3800,3802,3804,3806,3808,3810,3813,3815,3817],{"class":98,"line":2468},[96,3799,2567],{"class":110},[96,3801,1652],{"class":968},[96,3803,972],{"class":110},[96,3805,3760],{"class":968},[96,3807,972],{"class":110},[96,3809,920],{"class":919},[96,3811,3812],{"class":923},"1234 5678 1234 5678",[96,3814,920],{"class":919},[96,3816,3771],{"class":110},[96,3818,2649],{"class":114},[17,3820,3821,3822,3825,3826,3829,3830,3835],{},"This is the function you call in your form's ",[75,3823,3824],{},"onblur"," or ",[75,3827,3828],{},"onsubmit",". If you want to test online before integrating it, we have a ",[32,3831,3834],{"href":3832,"rel":3833},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcredit-card-validator",[36],"free online credit card validator"," that applies exactly this logic.",[24,3837,3839],{"id":3838},"what-this-algorithm-does-not-guarantee","What this algorithm does NOT guarantee",[17,3841,3842,3843,3846,3847,3850],{},"Many people skip this disclaimer and regret it later: ",[63,3844,3845],{},"the Luhn algorithm only validates the FORMAT\u002Fmathematical structure of the number",". It does ",[63,3848,3849],{},"NOT"," check:",[312,3852,3853,3856,3859,3862,3865],{},[315,3854,3855],{},"Whether the card actually exists",[315,3857,3858],{},"Whether it is active or blocked",[315,3860,3861],{},"Whether it has available credit",[315,3863,3864],{},"Whether it belongs to the person filling in the form",[315,3866,3867],{},"Whether it has been reported as stolen or used for fraud",[17,3869,3870,3871,3873],{},"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 ",[75,3872,3812],{}," to the gateway and getting a silly error that could have been caught earlier.",[17,3875,3876],{},"Don't write anything in your code suggesting that passing Luhn means \"a card you can charge\", because that is not true.",[24,3878,2941],{"id":2940},[17,3880,3881],{},"If you have implemented card validation before, you have probably seen (or made) one of these:",[844,3883,3885],{"id":3884},"validating-by-digit-count-only","Validating by digit count only",[17,3887,3888,3889,3892],{},"Many forms only check whether the number has 16 digits. That lets ",[75,3890,3891],{},"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.",[844,3894,3896],{"id":3895},"not-removing-spacesdashes-before-validating","Not removing spaces\u002Fdashes before validating",[17,3898,3899,3900,3825,3903,3905,3906,3909],{},"The user types ",[75,3901,3902],{},"4242-4242-4242-4242",[75,3904,3323],{},". If you don't clean that up before applying Luhn, validation fails even with the correct number. The line ",[75,3907,3908],{},"number.replace(\u002F\\D\u002Fg, '')"," solves this.",[844,3911,3913],{"id":3912},"treating-luhn-as-a-fraud-check","Treating Luhn as a fraud check",[17,3915,3916],{},"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.",[844,3918,3920],{"id":3919},"using-test-numbers-as-if-they-were-real","Using test numbers as if they were real",[17,3922,3923,3924,3927],{},"The test numbers that gateways like Stripe and PayPal provide pass Luhn, but ",[63,3925,3926],{},"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.",[17,3929,3930,3931,3936],{},"If you need Luhn-valid test data for your forms, our ",[32,3932,3935],{"href":3933,"rel":3934},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcredit-card-generator",[36],"credit card generator for testing"," creates structurally correct (but not real) numbers for you to use in QA.",[24,3938,1728],{"id":1727},[844,3940,3942],{"id":3941},"how-do-i-know-if-a-card-number-is-valid","How do I know if a card number is valid?",[17,3944,3945,3946,3949,3950,3953],{},"It depends on what you mean by \"valid\". If you mean ",[63,3947,3948],{},"correct format",", use the Luhn algorithm, which is what most gateways expect on the front-end. If you mean ",[63,3951,3952],{},"a card that actually works",", only the payment gateway (through the issuer) can answer that, after you try to charge it.",[844,3955,3957],{"id":3956},"can-i-test-a-payment-form-without-a-real-card","Can I test a payment form without a real card?",[17,3959,3960],{},"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.",[844,3962,3964],{"id":3963},"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?",[17,3966,3967],{},"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.",[844,3969,3971],{"id":3970},"what-is-the-luhn-algorithm","What is the Luhn algorithm?",[17,3973,3974],{},"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.",[844,3976,3978],{"id":3977},"can-i-use-luhn-to-prevent-fraud","Can I use Luhn to prevent fraud?",[17,3980,3981],{},"No. Luhn only catches typos, while fraud prevention requires behavioral analysis, address verification, 3D Secure and the gateway's risk rules.",[844,3983,3985],{"id":3984},"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\"?",[17,3987,3988],{},"\"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.",[24,3990,3992],{"id":3991},"wrapping-up","Wrapping up",[17,3994,3995],{},"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.",[17,3997,3998,3999,4003],{},"Want to test your implementation before shipping to production? Try our ",[32,4000,4002],{"href":3832,"rel":4001},[36],"online credit card validator",". It applies exactly the same logic you will use in your code.",[450,4005,4006],{},"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":92,"searchDepth":454,"depth":454,"links":4008},[4009,4010,4013,4014,4015,4021,4029],{"id":3299,"depth":454,"text":3300},{"id":3316,"depth":454,"text":3317,"children":4011},[4012],{"id":3366,"depth":951,"text":3367},{"id":2012,"depth":454,"text":2013},{"id":3838,"depth":454,"text":3839},{"id":2940,"depth":454,"text":2941,"children":4016},[4017,4018,4019,4020],{"id":3884,"depth":951,"text":3885},{"id":3895,"depth":951,"text":3896},{"id":3912,"depth":951,"text":3913},{"id":3919,"depth":951,"text":3920},{"id":1727,"depth":454,"text":1728,"children":4022},[4023,4024,4025,4026,4027,4028],{"id":3941,"depth":951,"text":3942},{"id":3956,"depth":951,"text":3957},{"id":3963,"depth":951,"text":3964},{"id":3970,"depth":951,"text":3971},{"id":3977,"depth":951,"text":3978},{"id":3984,"depth":951,"text":3985},{"id":3991,"depth":454,"text":3992},[468,3273],"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":3287,"description":4031},{"loc":4034,"lastmod":3279},"en\u002Fblog\u002Fhow-to-validate-credit-card-with-javascript",1317,"5gPXkTWBxMl6Rgba4_I126Dm2nIZeJpZMh9Vi5n2Zpc",{"id":4042,"title":4043,"articleCover":4044,"articleCoverEmbed":486,"articleCoverEmbedUploadDate":486,"articleCoverHeight":10,"articleCoverWidth":11,"authorId":12,"body":4045,"categoriesIds":4346,"description":4347,"enAlternateSlug":4348,"extension":471,"meta":4349,"navigation":473,"path":4350,"ptBrAlternateSlug":4351,"publishedAt":4352,"seo":4353,"sitemap":4354,"stem":4364,"updatedAt":1837,"wordCount":4365,"__hash__":4366},"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":14,"value":4046,"toc":4327},[4047,4050,4061,4065,4069,4077,4080,4083,4090,4094,4104,4108,4118,4122,4126,4135,4139,4148,4152,4156,4164,4168,4172,4180,4184,4188,4201,4205,4209,4213,4220,4223,4227,4230,4233,4237,4240,4243,4247,4261,4275,4279,4285,4292,4296,4299,4313,4317],[17,4048,4049],{},"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.",[17,4051,4052,4053,4060],{},"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 ",[63,4054,4055],{},[32,4056,4059],{"href":4057,"rel":4058},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools",[36],"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.",[24,4062,4064],{"id":4063},"what-tools-are-available-in-the-hub","What tools are available in the Hub?",[844,4066,4068],{"id":4067},"online-planning-poker","Online Planning Poker",[17,4070,4071,4076],{},[32,4072,4075],{"href":4073,"rel":4074},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fplanning-poker",[36],"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.",[17,4078,4079],{},"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.",[17,4081,4082],{},"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.",[4084,4085],"img",{"alt":4086,"height":4087,"src":4088,"width":4089},"Planning Poker tool interface",405,"\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-planning-poker-gratuito.webp",768,[844,4091,4093],{"id":4092},"cpf-generator-and-validator","CPF Generator and Validator",[17,4095,4096,4097,4103],{},"One of the most popular tools in the hub. The ",[63,4098,4099],{},[32,4100,4102],{"href":3211,"rel":4101},[36],"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.",[4084,4105],{"alt":4106,"height":4087,"src":4107,"width":4089},"CPF Generator tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-gerador-de-cpf.webp",[17,4109,4110,4111,4117],{},"As a complement, the ",[63,4112,4113],{},[32,4114,4116],{"href":2733,"rel":4115},[36],"CPF Validator"," was also created, which verifies if a number is structurally valid with immediate feedback.",[4084,4119],{"alt":4120,"height":4087,"src":4121,"width":4089},"CPF Validator tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-validador-de-cpf.webp",[844,4123,4125],{"id":4124},"credit-card-generator-and-validator","Credit Card Generator and Validator",[17,4127,1541,4128,4134],{},[63,4129,4130],{},[32,4131,4133],{"href":3933,"rel":4132},[36],"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.",[4084,4136],{"alt":4137,"height":4087,"src":4138,"width":4089},"Credit Card Generator tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-gerador-de-cartao-de-credito.webp",[17,4140,1541,4141,4147],{},[63,4142,4143],{},[32,4144,4146],{"href":3832,"rel":4145},[36],"Credit Card Validator"," automatically identifies the brand and verifies if the number is structurally valid. Essential for testing payment flows.",[4084,4149],{"alt":4150,"height":4087,"src":4151,"width":4089},"Credit Card Validator tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-validador-de-cartao-de-credito.webp",[844,4153,4155],{"id":4154},"character-and-word-counter","Character and Word Counter",[17,4157,4158,4163],{},[32,4159,4162],{"href":4160,"rel":4161},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fcharacter-and-word-counter",[36],"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.",[4084,4165],{"alt":4166,"height":4087,"src":4167,"width":4089},"Character and Word Counter tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-contador-de-caracteres-e-palavras.webp",[844,4169,4171],{"id":4170},"date-format-converter","Date Format Converter",[17,4173,4174,4179],{},[32,4175,4178],{"href":4176,"rel":4177},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fdate-format-converter",[36],"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.",[4084,4181],{"alt":4182,"height":4087,"src":4183,"width":4089},"Date Format Converter tool interface","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramentas-gerador-e-conversor-de-formato-de-datas.webp",[844,4185,4187],{"id":4186},"whatsapp-link-generator","WhatsApp Link Generator",[17,4189,4190,4191,4196,4197,4200],{},"Enter the phone number (with country code selection) and the desired message. ",[32,4192,4195],{"href":4193,"rel":4194},"https:\u002F\u002Fcodigoaoponto.com\u002Fen\u002Ftools\u002Fwhatsapp-message-url-generator",[36],"The tool"," generates a direct link to open the conversation on WhatsApp, with ",[63,4198,4199],{},"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\".",[4084,4202],{"alt":4203,"height":4087,"src":4204,"width":4089},"WhatsApp URL Generator tool interface with pre-filled text","\u002Fimages\u002Fblog\u002Fferramentas\u002Fferramenta-gerador-de-link-compartilhavel-no-whatsapp.webp",[24,4206,4208],{"id":4207},"design-and-usability-decisions","Design and Usability Decisions",[844,4210,4212],{"id":4211},"frictionless-no-barriers","Frictionless, No Barriers",[17,4214,4215,4216,4219],{},"The most important project decision was: ",[63,4217,4218],{},"no tool requires registration",". This was non-negotiable from the start. Access, use, done.",[17,4221,4222],{},"For Planning Poker, which is collaborative, you can create rooms with shareable links. No login, no password, no hassle.",[844,4224,4226],{"id":4225},"clean-and-intuitive-interface","Clean and Intuitive Interface",[17,4228,4229],{},"Each tool follows a consistent visual pattern: input area on the left, result on the right, and clearly visible action buttons.",[17,4231,4232],{},"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.",[844,4234,4236],{"id":4235},"accessible-in-portuguese-and-english","Accessible in Portuguese and English",[17,4238,4239],{},"From the beginning, I implemented internationalization. The system automatically detects the browser language, but you can manually switch whenever you want.",[17,4241,4242],{},"Portuguese to serve the Brazilian community, English to reach developers worldwide. The architecture is prepared to easily add new languages.",[844,4244,4246],{"id":4245},"responsive-and-fast","Responsive and Fast",[17,4248,4249,4250,4254,4255,4260],{},"All these tools have their ",[32,4251,4253],{"href":4057,"rel":4252},[36],"English versions with I18N"," and are tested with ",[32,4256,4259],{"href":4257,"rel":4258},"https:\u002F\u002Fpagespeed.web.dev\u002F",[36],"Lighthouse\u002FPageSpeed Insights"," to ensure good loading performance and visual stability.",[17,4262,4263,4264,156,4269,4274],{},"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 ",[32,4265,4268],{"href":4266,"rel":4267},"https:\u002F\u002Fdevelopers.google.com\u002Fanalytics",[36],"Google Analytics",[32,4270,4273],{"href":4271,"rel":4272},"https:\u002F\u002Fadsense.google.com\u002Fstart",[36],"Google Adsense",", but with proper optimizations, they deliver excellent loading speed.",[24,4276,4278],{"id":4277},"the-stack-behind-it","The Stack Behind It",[17,4280,4281,4284],{},[63,4282,4283],{},"Nuxt 4"," was chosen for the frontend for its focus on performance and SEO, very important aspects for good Google ranking.",[17,4286,4287,4288,4291],{},"For the backend, ",[63,4289,4290],{},"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.",[24,4293,4295],{"id":4294},"whats-coming-next","What's Coming Next",[17,4297,4298],{},"The backlog has some interesting ideas:",[312,4300,4301,4307],{},[315,4302,4303,4306],{},[63,4304,4305],{},"Sprint Retro"," for retrospective ceremonies",[315,4308,4309,4312],{},[63,4310,4311],{},"More utilities"," as the community suggests",[24,4314,4316],{"id":4315},"want-to-check-it-out","Want to Check It Out?",[17,4318,4319,4320,4326],{},"The tool has been helping hundreds of users daily, you can also be one of them ",[63,4321,4322],{},[32,4323,4325],{"href":4057,"rel":4324},[36],"by clicking here",", I hope to help you, thank you very much 🙋🏻",{"title":92,"searchDepth":454,"depth":454,"links":4328},[4329,4337,4343,4344,4345],{"id":4063,"depth":454,"text":4064,"children":4330},[4331,4332,4333,4334,4335,4336],{"id":4067,"depth":951,"text":4068},{"id":4092,"depth":951,"text":4093},{"id":4124,"depth":951,"text":4125},{"id":4154,"depth":951,"text":4155},{"id":4170,"depth":951,"text":4171},{"id":4186,"depth":951,"text":4187},{"id":4207,"depth":454,"text":4208,"children":4338},[4339,4340,4341,4342],{"id":4211,"depth":951,"text":4212},{"id":4225,"depth":951,"text":4226},{"id":4235,"depth":951,"text":4236},{"id":4245,"depth":951,"text":4246},{"id":4277,"depth":454,"text":4278},{"id":4294,"depth":454,"text":4295},{"id":4315,"depth":454,"text":4316},[3273],"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":4043,"description":4347},{"loc":4350,"images":4355,"lastmod":1837},[4356,4357,4358,4359,4360,4361,4362,4363],{"loc":4088},{"loc":4107},{"loc":4121},{"loc":4138},{"loc":4151},{"loc":4167},{"loc":4183},{"loc":4204},"en\u002Fblog\u002Ffree-developer-tools-hub",952,"TaF5lI57uyi5DI04hKg3rGxUwsitM4_NctybZAcquMI",1793928189059]