[{"data":1,"prerenderedAt":1064},["ShallowReactive",2],{"content:\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fusing-pure-annotations-for-side-effect-free-calls":3,"surroundings:\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fusing-pure-annotations-for-side-effect-free-calls":1055},{"id":4,"title":5,"body":6,"description":1034,"extension":1035,"meta":1036,"navigation":1048,"path":1049,"seo":1050,"stem":1053,"__hash__":1054},"content\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fusing-pure-annotations-for-side-effect-free-calls\u002Findex.md","Using PURE Annotations for Side-Effect-Free Calls",{"type":7,"value":8,"toc":1017},"minimark",[9,23,65,76,183,188,231,235,241,259,265,274,345,349,354,454,457,461,567,570,574,589,643,646,766,773,786,790,798,802,823,827,857,861,874,880,890,902,906,925,945,957,966,975,979,1002,1007,1010,1013],[10,11,13,14,22],"h1",{"id":12},"how-to-use-pure-annotations-for-side-effect-free-calls","How to Use \u002F",[15,16,17,18],"em",{},"#",[19,20,21],"strong",{},"PURE","\u002F Annotations for Side-Effect-Free Calls",[24,25,26,27,32,33,37,38,42,43,46,47,50,51,50,54,50,57,60,61,64],"p",{},"This guide covers a precise tool in ",[28,29,31],"a",{"href":30},"\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002F","Tree Shaking and Dead Code Elimination",", part of ",[28,34,36],{"href":35},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript Bundle Optimization & Code Splitting",". Bundlers can remove an unused ",[39,40,41],"code",{},"export const Button = ..."," when the right-hand side is a literal or a function expression. When it is a ",[15,44,45],{},"call"," — ",[39,48,49],{},"createIcon(...)",", ",[39,52,53],{},"styled.button(...)",[39,55,56],{},"defineComponent(...)",[39,58,59],{},"memo(Component)"," — the bundler cannot know whether the call has side effects, so it keeps it, along with everything the call references, even if ",[39,62,63],{},"Button"," is never imported.",[24,66,67,68,71,72,75],{},"The ",[39,69,70],{},"\u002F*#__PURE__*\u002F"," (or ",[39,73,74],{},"\u002F*@__PURE__*\u002F",") comment placed immediately before a call tells bundlers and minifiers that the call has no side effects: if its result is unused, the whole call can be removed. It is understood by Rollup, webpack (via Terser), esbuild, SWC and Terser. Compilers like Babel's React preset and styled-components' Babel plugin emit it automatically for JSX and styled declarations; hand-written factory code usually lacks it.",[24,77,78,162,166,167,86,169,86,173,86,175,86,178,86,180,86],{},[79,80,86,87,86,94,86,98,86,101,86,110,86,116,86,125,86,131,86,136,86,141,86,144,86,147,86,150,86,153,86,158],"svg",{"viewBox":81,"width":82,"role":83,"ariaLabel":84,"style":85},"0 0 760 204","100%","img","Comparison of how a bundler treats an unused export initialised by a function call, with and without a PURE annotation.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[88,89],"rect",{"className":90,"x":92,"y":92,"width":82,"height":82,"fill":93},[91],"svg-canvas","0","#ffffff",[95,96,97],"title",{},"An unused export with and without the annotation",[99,100,84],"desc",{},[88,102],{"x":103,"y":103,"width":104,"height":105,"rx":106,"fill":107,"stroke":108,"style":109},"1","758","202","10","none","currentColor","stroke-opacity:0.18",[111,112,97],"text",{"x":113,"y":114,"fill":108,"style":115},"28.0","34.0","font-size:16px;font-weight:700",[88,117],{"x":113,"y":118,"width":119,"height":120,"rx":121,"fill":122,"stroke":123,"style":124},"56.0","340.0","126.0","6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[111,126,130],{"x":127,"y":128,"fill":108,"style":129},"42.0","82.0","font-size:14px;font-weight:700","export const X = factory()",[111,132,135],{"x":127,"y":133,"fill":108,"style":134},"108.0","font-size:12.5px;font-weight:700","•",[111,137,140],{"x":118,"y":133,"fill":108,"style":138,"textAnchor":139},"font-size:12.5px","start","Call might have side effects",[111,142,135],{"x":127,"y":143,"fill":108,"style":134},"132.0",[111,145,146],{"x":118,"y":143,"fill":108,"style":138,"textAnchor":139},"Kept even if X is unused",[111,148,135],{"x":127,"y":149,"fill":108,"style":134},"156.0",[111,151,152],{"x":118,"y":149,"fill":108,"style":138,"textAnchor":139},"factory and its dependencies kept",[88,154],{"x":155,"y":118,"width":119,"height":120,"rx":121,"fill":156,"stroke":156,"style":157},"392.0","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[111,159,161],{"x":160,"y":128,"fill":108,"style":129},"406.0","export const X = \u002F",[15,163,17,164],{},[19,165,21],{},"\u002F factory() ",[111,168,135],{"x":160,"y":133,"fill":108,"style":134},[111,170,172],{"x":171,"y":133,"fill":108,"style":138,"text-anchor":139},"420.0","Call declared side-effect free",[111,174,135],{"x":160,"y":143,"fill":108,"style":134},[111,176,177],{"x":171,"y":143,"fill":108,"style":138,"text-anchor":139},"Dropped if X is unused",[111,179,135],{"x":160,"y":149,"fill":108,"style":134},[111,181,182],{"x":171,"y":149,"fill":108,"style":138,"text-anchor":139},"factory can be removed too if unused elsewhere",[184,185,187],"h2",{"id":186},"rapid-diagnosis","Rapid Diagnosis",[189,190,191,202,212,225],"ul",{},[192,193,194,197,198,201],"li",{},[19,195,196],{},"Find module-scope calls in shared libraries."," Search for ",[39,199,200],{},"export const \\w+ = \\w+\\("," patterns in design systems, icon packages and utility libraries.",[192,203,204,207,208,211],{},[19,205,206],{},"Test removal."," Import one export from the module in a test entry; if other exports' factory calls remain in the output, annotations (or ",[39,209,210],{},"sideEffects",") are missing.",[192,213,214,217,218,50,221,224],{},[19,215,216],{},"Check generated code."," Compiled output from Babel or TypeScript may wrap expressions in helpers (",[39,219,220],{},"_interopRequireDefault(...)",[39,222,223],{},"__decorate(...)",") without annotations.",[192,226,227,230],{},[19,228,229],{},"Check the minifier output."," With annotations present, minifiers drop unused calls even within a single chunk.",[184,232,234],{"id":233},"root-cause-analysis","Root Cause Analysis",[24,236,237,240],{},[19,238,239],{},"1. Calls are presumed effectful."," Any call could mutate globals, register things or perform I\u002FO; tools must be conservative.",[24,242,243,86,246,50,249,50,252,50,255,258],{},[19,244,245],{},"2. Higher-order components and factories.",[39,247,248],{},"memo()",[39,250,251],{},"forwardRef()",[39,253,254],{},"connect()(...)",[39,256,257],{},"withStyles()(...)"," and component factories all produce values through calls.",[24,260,261,264],{},[19,262,263],{},"3. Compiled helpers."," Transpilers inject helper calls (class creation, decorators, interop) that are not annotated.",[24,266,267,86,270,273],{},[19,268,269],{},"4. Static class members.",[39,271,272],{},"class A { static config = buildConfig(); }"," runs the call when the class is defined — effectively module-scope code.",[24,275,276],{},[79,277,86,280,86,283,86,286,86,288,86,291,86,293,86,300,86,307,86,312,86,316,86,320,86,324,86,327,86,332,86,336,86,341,86],{"viewBox":278,"width":82,"role":83,"ariaLabel":279,"style":85},"0 0 760 207","Bar chart of bundle size for a generated icon package with and without PURE annotations on its factory calls.",[88,281],{"className":282,"x":92,"y":92,"width":82,"height":82,"fill":93},[91],[95,284,285],{},"Icon package output when importing 12 of 1,400 icons",[99,287,279],{},[88,289],{"x":103,"y":103,"width":104,"height":290,"rx":106,"fill":107,"stroke":108,"style":109},"205",[111,292,285],{"x":113,"y":114,"fill":108,"style":115},[111,294,299],{"x":295,"y":296,"fill":108,"style":297,"textAnchor":298},"193.6","70.0","font-size:13px","end","No annotations",[88,301],{"x":302,"y":118,"width":303,"height":304,"rx":305,"fill":122,"stroke":123,"style":306},"205.6","462.4","19","3","fill-opacity:0.7;stroke-opacity:0.9",[111,308,311],{"x":309,"y":296,"fill":108,"style":310},"674.0","font-size:12px;font-weight:600","298KB",[111,313,315],{"x":295,"y":314,"fill":108,"style":297,"textAnchor":298},"101.0","sideEffects false only",[88,317],{"x":302,"y":318,"width":319,"height":304,"rx":305,"fill":122,"stroke":123,"style":306},"87.0","459.3",[111,321,323],{"x":322,"y":314,"fill":108,"style":310},"670.9","296KB",[111,325,326],{"x":295,"y":143,"fill":108,"style":297,"textAnchor":298},"PURE on createIcon calls",[88,328],{"x":302,"y":329,"width":330,"height":304,"rx":305,"fill":156,"stroke":156,"style":331},"118.0","7.8","fill-opacity:0.55;stroke-opacity:0.9",[111,333,335],{"x":334,"y":143,"fill":108,"style":310},"219.4","5KB",[111,337,340],{"x":113,"y":338,"fill":339,"style":138},"167.0","#51617a","sideEffects alone did not help because all icons live in one module; the PURE annotations let the minifier drop the",[111,342,344],{"x":113,"y":343,"fill":339,"style":138},"185.0","unused calls within it.",[184,346,348],{"id":347},"step-by-step-resolution","Step-by-Step Resolution",[350,351,353],"h3",{"id":352},"_1-annotate-module-scope-factory-calls","1. Annotate module-scope factory calls",[355,356,361],"pre",{"className":357,"code":358,"language":359,"meta":360,"style":360},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F icons.js — generated: one export per icon.\nexport const SearchIcon = \u002F*#__PURE__*\u002F createIcon('search', 'M10 2a8 8 0 1 0 5.3 14l5 5 1.4-1.4-5-5A8 8 0 0 0 10 2z');\nexport const CloseIcon  = \u002F*#__PURE__*\u002F createIcon('close',  'M5 5l14 14M19 5L5 19');\n\u002F\u002F trade-off: the annotation is a promise. If createIcon ever starts registering\n\u002F\u002F icons globally, unused icons will silently disappear from the registry.\n","javascript","",[39,362,363,372,412,442,448],{"__ignoreMap":360},[364,365,368],"span",{"class":366,"line":367},"line",1,[364,369,371],{"class":370},"sjfSM","\u002F\u002F icons.js — generated: one export per icon.\n",[364,373,375,379,382,386,389,392,396,400,404,406,409],{"class":366,"line":374},2,[364,376,378],{"class":377},"sPARh","export",[364,380,381],{"class":377}," const",[364,383,385],{"class":384},"sPXB4"," SearchIcon",[364,387,388],{"class":377}," =",[364,390,391],{"class":370}," \u002F*#__PURE__*\u002F",[364,393,395],{"class":394},"smZ65"," createIcon",[364,397,399],{"class":398},"saISM","(",[364,401,403],{"class":402},"sZ8jY","'search'",[364,405,50],{"class":398},[364,407,408],{"class":402},"'M10 2a8 8 0 1 0 5.3 14l5 5 1.4-1.4-5-5A8 8 0 0 0 10 2z'",[364,410,411],{"class":398},");\n",[364,413,415,417,419,422,425,427,429,431,434,437,440],{"class":366,"line":414},3,[364,416,378],{"class":377},[364,418,381],{"class":377},[364,420,421],{"class":384}," CloseIcon",[364,423,424],{"class":377},"  =",[364,426,391],{"class":370},[364,428,395],{"class":394},[364,430,399],{"class":398},[364,432,433],{"class":402},"'close'",[364,435,436],{"class":398},",  ",[364,438,439],{"class":402},"'M5 5l14 14M19 5L5 19'",[364,441,411],{"class":398},[364,443,445],{"class":366,"line":444},4,[364,446,447],{"class":370},"\u002F\u002F trade-off: the annotation is a promise. If createIcon ever starts registering\n",[364,449,451],{"class":366,"line":450},5,[364,452,453],{"class":370},"\u002F\u002F icons globally, unused icons will silently disappear from the registry.\n",[24,455,456],{},"Expected outcome: unused icons, components or styled declarations are removed by the minifier.",[350,458,460],{"id":459},"_2-annotate-higher-order-component-wrappers","2. Annotate higher-order component wrappers",[355,462,464],{"className":357,"code":463,"language":359,"meta":360,"style":360},"export const Card = \u002F*#__PURE__*\u002F memo(\u002F*#__PURE__*\u002F forwardRef(function Card(props, ref) {\n  return \u003Cdiv ref={ref} className=\"card\" {...props} \u002F>;\n}));\n\u002F\u002F trade-off: annotations must sit directly before each call expression; an\n\u002F\u002F annotation before memo() does not cover the inner forwardRef() call.\n",[39,465,466,510,552,557,562],{"__ignoreMap":360},[364,467,468,470,472,475,477,479,482,484,486,489,491,494,496,498,502,504,507],{"class":366,"line":367},[364,469,378],{"class":377},[364,471,381],{"class":377},[364,473,474],{"class":384}," Card",[364,476,388],{"class":377},[364,478,391],{"class":370},[364,480,481],{"class":394}," memo",[364,483,399],{"class":398},[364,485,70],{"class":370},[364,487,488],{"class":394}," forwardRef",[364,490,399],{"class":398},[364,492,493],{"class":377},"function",[364,495,474],{"class":394},[364,497,399],{"class":398},[364,499,501],{"class":500},"sQw3B","props",[364,503,50],{"class":398},[364,505,506],{"class":500},"ref",[364,508,509],{"class":398},") {\n",[364,511,512,515,518,522,525,528,530,533,536,539,542,545,547,549],{"class":366,"line":374},[364,513,514],{"class":377},"  return",[364,516,517],{"class":398}," \u003C",[364,519,521],{"class":520},"sZBmE","div",[364,523,524],{"class":384}," ref",[364,526,527],{"class":377},"={",[364,529,506],{"class":398},[364,531,532],{"class":377},"}",[364,534,535],{"class":384}," className",[364,537,538],{"class":377},"=",[364,540,541],{"class":402},"\"card\"",[364,543,544],{"class":377}," {...",[364,546,501],{"class":398},[364,548,532],{"class":377},[364,550,551],{"class":398}," \u002F>;\n",[364,553,554],{"class":366,"line":414},[364,555,556],{"class":398},"}));\n",[364,558,559],{"class":366,"line":444},[364,560,561],{"class":370},"\u002F\u002F trade-off: annotations must sit directly before each call expression; an\n",[364,563,564],{"class":366,"line":450},[364,565,566],{"class":370},"\u002F\u002F annotation before memo() does not cover the inner forwardRef() call.\n",[24,568,569],{},"Expected outcome: unused wrapped components are dropped.",[350,571,573],{"id":572},"_3-let-tooling-add-annotations-where-it-can","3. Let tooling add annotations where it can",[24,575,576,577,580,581,584,585,588],{},"Enable compiler options that emit annotations automatically: React's JSX runtime calls are annotated; ",[39,578,579],{},"babel-plugin-styled-components"," (with ",[39,582,583],{},"pure: true",") and the SWC styled-components plugin annotate styled declarations; Rollup's ",[39,586,587],{},"treeshake.manualPureFunctions"," lets you declare functions as pure by name without editing call sites.",[355,590,592],{"className":357,"code":591,"language":359,"meta":360,"style":360},"\u002F\u002F rollup \u002F vite config\nexport default { build: { rollupOptions: { treeshake: { manualPureFunctions: ['createIcon', 'styled', 'defineMessages'] } } } };\n\u002F\u002F trade-off: manualPureFunctions applies to every call of those names in the\n\u002F\u002F build, including third-party code that might use the same name differently.\n",[39,593,594,599,633,638],{"__ignoreMap":360},[364,595,596],{"class":366,"line":367},[364,597,598],{"class":370},"\u002F\u002F rollup \u002F vite config\n",[364,600,601,603,606,609,612,615,617,620,622,625,628,630],{"class":366,"line":374},[364,602,378],{"class":377},[364,604,605],{"class":377}," default",[364,607,608],{"class":500}," { ",[364,610,611],{"class":398},"build: { rollupOptions: { treeshake: { manualPureFunctions: [",[364,613,614],{"class":402},"'createIcon'",[364,616,50],{"class":398},[364,618,619],{"class":402},"'styled'",[364,621,50],{"class":398},[364,623,624],{"class":402},"'defineMessages'",[364,626,627],{"class":398},"] } } } ",[364,629,532],{"class":500},[364,631,632],{"class":398},";\n",[364,634,635],{"class":366,"line":414},[364,636,637],{"class":370},"\u002F\u002F trade-off: manualPureFunctions applies to every call of those names in the\n",[364,639,640],{"class":366,"line":444},[364,641,642],{"class":370},"\u002F\u002F build, including third-party code that might use the same name differently.\n",[24,644,645],{},"Expected outcome: annotations without touching every file.",[24,647,648],{},[79,649,86,652,86,655,86,658,86,660,86,663,86,665,86,670,86,675,86,679,86,684,86,687,86,691,86,696,86,700,86,704,86,706,86,711,86,713,86,716,86,718,86,722,86,724,86,727,86,729,86,732,86,735,86,739,86,741,86,744,86,746,86,749,86,752,86,756,86,758,86,761,86,763,86],{"viewBox":650,"width":82,"role":83,"ariaLabel":651,"style":85},"0 0 760 244","Sources of PURE annotations in a typical build and whether they are automatic.",[88,653],{"className":654,"x":92,"y":92,"width":82,"height":82,"fill":93},[91],[95,656,657],{},"Where PURE annotations come from",[99,659,651],{},[88,661],{"x":103,"y":103,"width":104,"height":662,"rx":106,"fill":107,"stroke":108,"style":109},"242",[111,664,657],{"x":113,"y":114,"fill":108,"style":115},[88,666],{"x":113,"y":118,"width":667,"height":668,"rx":92,"fill":108,"stroke":108,"style":669},"210.0","30.0","fill-opacity:0.06;stroke-opacity:0.4",[111,671,674],{"x":672,"y":673,"fill":108,"style":134,"textAnchor":139},"38.0","75.5","Source",[88,676],{"x":677,"y":118,"width":678,"height":668,"rx":92,"fill":108,"stroke":108,"style":669},"238.0","247.0",[111,680,683],{"x":681,"y":673,"fill":108,"style":134,"textAnchor":682},"361.5","middle","Automatic?",[88,685],{"x":686,"y":118,"width":678,"height":668,"rx":92,"fill":108,"stroke":108,"style":669},"485.0",[111,688,690],{"x":689,"y":673,"fill":108,"style":134,"textAnchor":682},"608.5","Notes",[88,692],{"x":113,"y":693,"width":667,"height":694,"rx":92,"fill":107,"stroke":108,"style":695},"86.0","46.0","stroke-opacity:0.35",[111,697,699],{"x":672,"y":698,"fill":108,"style":134,"textAnchor":139},"105.5","JSX compiled by",[111,701,703],{"x":672,"y":702,"fill":108,"style":134,"textAnchor":139},"121.5","Babel\u002FSWC\u002Fesbuild",[88,705],{"x":677,"y":693,"width":678,"height":694,"rx":92,"fill":156,"stroke":156,"style":157},[111,707,710],{"x":681,"y":708,"fill":108,"style":709,"textAnchor":682},"113.5","font-size:12px","yes",[88,712],{"x":686,"y":693,"width":678,"height":694,"rx":92,"fill":107,"stroke":108,"style":695},[111,714,715],{"x":689,"y":708,"fill":108,"style":709,"textAnchor":682},"Element creation calls annotated",[88,717],{"x":113,"y":143,"width":667,"height":668,"rx":92,"fill":107,"stroke":108,"style":695},[111,719,721],{"x":672,"y":720,"fill":108,"style":134,"textAnchor":139},"151.5","styled-components plugin",[88,723],{"x":677,"y":143,"width":678,"height":668,"rx":92,"fill":108,"stroke":108,"style":669},[111,725,726],{"x":681,"y":720,"fill":108,"style":709,"textAnchor":682},"with pure option",[88,728],{"x":686,"y":143,"width":678,"height":668,"rx":92,"fill":107,"stroke":108,"style":695},[111,730,731],{"x":689,"y":720,"fill":108,"style":709,"textAnchor":682},"Babel or SWC plugin",[88,733],{"x":113,"y":734,"width":667,"height":668,"rx":92,"fill":107,"stroke":108,"style":695},"162.0",[111,736,738],{"x":672,"y":737,"fill":108,"style":134,"textAnchor":139},"181.5","Rollup manualPureFunctions",[88,740],{"x":677,"y":734,"width":678,"height":668,"rx":92,"fill":108,"stroke":108,"style":669},[111,742,743],{"x":681,"y":737,"fill":108,"style":709,"textAnchor":682},"config",[88,745],{"x":686,"y":734,"width":678,"height":668,"rx":92,"fill":107,"stroke":108,"style":695},[111,747,748],{"x":689,"y":737,"fill":108,"style":709,"textAnchor":682},"By function name",[88,750],{"x":113,"y":751,"width":667,"height":668,"rx":92,"fill":107,"stroke":108,"style":695},"192.0",[111,753,755],{"x":672,"y":754,"fill":108,"style":134,"textAnchor":139},"211.5","Hand-written factories",[88,757],{"x":677,"y":751,"width":678,"height":668,"rx":92,"fill":122,"stroke":123,"style":124},[111,759,760],{"x":681,"y":754,"fill":108,"style":709,"textAnchor":682},"no",[88,762],{"x":686,"y":751,"width":678,"height":668,"rx":92,"fill":107,"stroke":108,"style":695},[111,764,765],{"x":689,"y":754,"fill":108,"style":709,"textAnchor":682},"Add comments or rely on config",[350,767,769,770,772],{"id":768},"_4-combine-with-sideeffects-for-file-level-removal","4. Combine with ",[39,771,210],{}," for file-level removal",[24,774,775,776,779,780,782,783,785],{},"Annotations work ",[15,777,778],{},"within"," modules; ",[39,781,210],{}," works on whole modules. Large libraries need both: ",[39,784,210],{}," so unused files are skipped, annotations so unused exports inside used files are dropped.",[184,787,789],{"id":788},"verification","Verification",[24,791,792,793,797],{},"Build a fixture that imports one export and inspect the minified output: other annotated calls should be gone. Run your test suite against the production build to confirm no behaviour depended on a removed call. For libraries, add the fixture to CI as described in ",[28,794,796],{"href":795},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fvite-and-rollup-build-optimization\u002Fbuilding-tree-shakeable-libraries-with-vite-library-mode\u002F","building tree-shakeable libraries with Vite library mode",".",[184,799,801],{"id":800},"worked-example-a-message-catalogue","Worked Example: A Message Catalogue",[24,803,804,805,808,809,812,813,815,816,819,820,822],{},"An internationalisation library defined messages with ",[39,806,807],{},"export const greeting = defineMessage({ id: 'greeting', defaultMessage: 'Hello' })"," across hundreds of modules. Each ",[39,810,811],{},"defineMessage"," call simply returned its argument, but bundlers kept every one, along with the default strings, inflating each route by tens of kilobytes of unused messages. Adding ",[39,814,811],{}," to ",[39,817,818],{},"manualPureFunctions"," removed unused messages from every route without editing a single call site. The team confirmed with the library authors that ",[39,821,811],{}," had no side effects before relying on it.",[184,824,826],{"id":825},"common-mistakes","Common Mistakes",[189,828,829,835,845,851],{},[192,830,831,834],{},[19,832,833],{},"Annotating calls that do have side effects."," The code disappears and the side effect with it — a subtle, production-only bug.",[192,836,837,840,841,844],{},[19,838,839],{},"Placing the comment in the wrong spot."," It must immediately precede the call expression (",[39,842,843],{},"\u002F*#__PURE__*\u002F fn()","), not the declaration or the export.",[192,846,847,850],{},[19,848,849],{},"Losing annotations in your own build."," Some minifier or transpiler settings strip comments before bundling; check that annotations survive into the bundler's input.",[192,852,853,856],{},[19,854,855],{},"Expecting annotations to remove used code."," They only affect calls whose results are unused.",[184,858,860],{"id":859},"edge-cases-worth-knowing","Edge Cases Worth Knowing",[24,862,863,86,870,873],{},[19,864,865,866,869],{},"Annotations on ",[39,867,868],{},"new"," expressions.",[39,871,872],{},"\u002F*#__PURE__*\u002F new Map()"," is valid and lets minifiers drop unused instances — useful for module-level caches and registries that may never be used.",[24,875,876,879],{},[19,877,878],{},"Annotations lost in transpilation."," Some transforms rewrite call expressions (for example, optional-call lowering or decorator transforms) and drop leading comments. If annotations seem ignored, inspect the code the bundler actually receives, not your source.",[24,881,882,885,886,889],{},[19,883,884],{},"Arguments with side effects."," An annotated call is removed together with its arguments. If an argument expression itself has side effects — ",[39,887,888],{},"\u002F*#__PURE__*\u002F wrap(registerGlobal())"," — that side effect disappears too. Keep arguments to annotated calls free of effects.",[24,891,892,897,898,901],{},[19,893,894,895,797],{},"Interaction with ",[39,896,210],{}," A module listed as side-effect free can be dropped entirely if none of its exports are used, regardless of annotations. Annotations matter for modules that ",[15,899,900],{},"are"," included because some of their exports are used — they remove the unused rest.",[184,903,905],{"id":904},"faq","FAQ",[907,908,911,915],"details",{"className":909},[910],"faq-item",[912,913,914],"summary",{},"Is \u002F*@__PURE__*\u002F different from \u002F*#__PURE__*\u002F?",[24,916,917,918,920,921,924],{},"They are equivalent; tools accept both. The ",[39,919,17],{}," form avoids clashing with JSDoc-style ",[39,922,923],{},"@"," tags and is the more common convention today.",[907,926,928,931],{"className":927},[910],[912,929,930],{},"What about the newer __NO_SIDE_EFFECTS__ annotation?",[24,932,933,934,937,938,941,942,944],{},"Rollup and some other tools support ",[39,935,936],{},"\u002F*#__NO_SIDE_EFFECTS__*\u002F"," on a function ",[15,939,940],{},"declaration",", marking every call of that function as pure — the declaration-level equivalent of annotating each call. Support is less universal than ",[39,943,21],{},", so check your toolchain before relying on it.",[907,946,948,951],{"className":947},[910],[912,949,950],{},"Do annotations help webpack without Terser?",[24,952,953,954,956],{},"webpack's own module-level analysis uses ",[39,955,210],{},"; removing an unused annotated call within a module is done by the minifier (Terser or esbuild via a plugin). Without minification, annotated but unused calls remain in the output.",[907,958,960,963],{"className":959},[910],[912,961,962],{},"Can a linter check that annotated functions really are pure?",[24,964,965],{},"Not in general — purity is a semantic property. What helps is convention: keep annotated factories in a small, reviewed module, document that they must not touch globals, and add unit tests asserting that calling them leaves no observable state behind (no registry entries, no globals). Review changes to those factories with the annotation in mind.",[907,967,969,972],{"className":968},[910],[912,970,971],{},"Do annotations affect development builds?",[24,973,974],{},"No in practice. Development builds typically skip minification and aggressive tree shaking, so annotated unused calls remain and run as normal. That is another reason to test the production build: a call that should not have been annotated only disappears there.",[184,976,978],{"id":977},"related","Related",[189,980,981,988,995],{},[192,982,983,987],{},[28,984,986],{"href":985},"\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fwhy-barrel-files-break-tree-shaking\u002F","Why barrel files break tree shaking"," — the file-level counterpart.",[192,989,990,994],{},[28,991,993],{"href":992},"\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fesbuild-vs-terser-for-production-minification\u002F","esbuild vs Terser for production minification"," — the minifiers that act on annotations.",[192,996,997,1001],{},[28,998,1000],{"href":999},"\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fwriting-tree-shakeable-library-exports\u002F","Writing tree-shakeable library exports"," — export patterns that need fewer annotations.",[1003,1004,1006],"script",{"type":1005},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Use \u002F#PURE__*\u002F Annotations for Side-Effect-Free Calls\",\n  \"description\": \"How PURE annotations let bundlers remove unused results of function calls at module scope, where they help, and where they cause bugs.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Annotate module-scope factory calls\",\n      \"text\": \"Expected outcome: unused icons, components or styled declarations are removed by the minifier.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Annotate higher-order component wrappers\",\n      \"text\": \"Expected outcome: unused wrapped components are dropped.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Let tooling add annotations where it can\",\n      \"text\": \"Enable compiler options that emit annotations automatically: React's JSX runtime calls are annotated; babel-plugin-styled-components (with pure: true) and the SWC styled-components plugin annotate styled declarations; Rollup's treeshake.manualPureFunctions lets you declare functions as pure by name \"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Combine with sideEffects for file-level removal\",\n      \"text\": \"Annotations work within modules; sideEffects works on whole modules.\"\n    }\n  ]\n}\n",[1003,1008,1009],{"type":1005},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Use \u002F#PURE__*\u002F Annotations for Side-Effect-Free Calls\",\n  \"description\": \"How PURE annotations let bundlers remove unused results of function calls at module scope, where they help, and where they cause bugs.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fusing-pure-annotations-for-side-effect-free-calls\u002F\"\n  }\n}\n",[1003,1011,1012],{"type":1005},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"JavaScript Bundle Optimization & Code Splitting\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Tree Shaking and Dead Code Elimination\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Using PURE Annotations for Side-Effect-Free Calls\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fusing-pure-annotations-for-side-effect-free-calls\u002F\"\n    }\n  ]\n}\n",[1014,1015,1016],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}",{"title":360,"searchDepth":374,"depth":374,"links":1018},[1019,1020,1021,1028,1029,1030,1031,1032,1033],{"id":186,"depth":374,"text":187},{"id":233,"depth":374,"text":234},{"id":347,"depth":374,"text":348,"children":1022},[1023,1024,1025,1026],{"id":352,"depth":414,"text":353},{"id":459,"depth":414,"text":460},{"id":572,"depth":414,"text":573},{"id":768,"depth":414,"text":1027},"4. Combine with sideEffects for file-level removal",{"id":788,"depth":374,"text":789},{"id":800,"depth":374,"text":801},{"id":825,"depth":374,"text":826},{"id":859,"depth":374,"text":860},{"id":904,"depth":374,"text":905},{"id":977,"depth":374,"text":978},"How PURE annotations let bundlers remove unused results of function calls at module scope, where they help, and where they cause bugs.","md",{"slug":1037,"type":1038,"breadcrumb":1039,"datePublished":1047,"dateModified":1047},"using-pure-annotations-for-side-effect-free-calls","article",[1040,1043,1044,1045],{"name":1041,"url":1042},"Home","\u002F",{"name":36,"url":35},{"name":31,"url":30},{"name":5,"url":1046},"\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fusing-pure-annotations-for-side-effect-free-calls\u002F","2026-10-06",true,"\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fusing-pure-annotations-for-side-effect-free-calls",{"title":1051,"description":1052},"Using \u002F*#__PURE__*\u002F Annotations for Tree Shaking","Mark side-effect-free function calls with \u002F*#__PURE__*\u002F so bundlers and minifiers can drop unused results — factory calls, HOCs and styled components.","javascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fusing-pure-annotations-for-side-effect-free-calls\u002Findex","3SJTZKU6XTOUL2WgiwflkbP2AxKMX9kNUX3WySmJhBc",[1056,1060],{"title":1057,"path":1058,"stem":1059,"children":-1},"sideEffects: false Broke My CSS Imports","\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fsideeffects-false-broke-my-css-imports","javascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fsideeffects-false-broke-my-css-imports\u002Findex",{"title":1061,"path":1062,"stem":1063,"children":-1},"Why Barrel Files Break Tree Shaking","\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fwhy-barrel-files-break-tree-shaking","javascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Fwhy-barrel-files-break-tree-shaking\u002Findex",1791308076309]