Overview
Argo CD’s web interface can be extended with custom UI elements written in JavaScript/React. Extensions are loaded from JavaScript files placed in the argocd-server Pods.
Extension Types Argo CD supports several extension points:
Resource tab extensions
Application tab extensions
System-level extensions
Status panel extensions
Top bar action menu extensions
App view extensions
Installation
Place extension JavaScript files in /tmp/extensions directory of argocd-server Pods:
/tmp/extensions
├── example1
│ └── extension-1.js
└── example2
└── extension-2.js
File naming requirement: Extension files must match the regex ^extension(.*)\.js$
Mount extensions using a ConfigMap or build them into a custom argocd-server image.
Extension Basics
JavaScript Structure
Extensions register themselves using the extensionsAPI global variable:
(( window ) => {
const component = () => {
return React . createElement ( "div" , {}, "Hello World" );
};
window . extensionsAPI . registerResourceExtension (
component ,
"*" ,
"*" ,
"My Extension"
);
})( window );
Using React
Don’t Bundle React Extensions must use the React global variable, not bundle React. Configure webpack: externals : {
react : "React"
}
Resource Tab Extensions
Add custom tabs to the resource sliding panel on the Application details page.
registerResourceExtension (
component : ExtensionComponent ,
group : string ,
kind : string ,
tabTitle : string ,
opts ?: { icon? : string }
)
Component receives props:
application: Application - Argo CD Application resource
resource: State - The Kubernetes resource object
tree: ApplicationTree - List of all resources in the application
Example
(( window ) => {
const component = ( props ) => {
const { application , resource , tree } = props ;
return React . createElement ( "div" , { style: { padding: "20px" } }, [
React . createElement ( "h3" , {}, "Resource Details" ),
React . createElement ( "p" , {}, `Name: ${ resource . name } ` ),
React . createElement ( "p" , {}, `Kind: ${ resource . kind } ` ),
React . createElement ( "p" , {}, `App: ${ application . metadata . name } ` )
]);
};
window . extensionsAPI . registerResourceExtension (
component ,
"apps" , // Group glob pattern
"Deployment" , // Kind glob pattern
"Custom Info" , // Tab title
{ icon: "fa-info-circle" }
);
})( window );
Glob Patterns
Use glob expressions to match multiple resource types:
// Match all resources
window . extensionsAPI . registerResourceExtension (
component ,
"**" , // All groups (including empty)
"*" , // All kinds
"Universal Tab"
);
// Match specific API group
window . extensionsAPI . registerResourceExtension (
component ,
"apps" ,
"*" ,
"Apps Resources"
);
Application Tab Extensions
Since Applications are Kubernetes resources, use registerResourceExtension with:
group: argoproj.io
kind: Application
window . extensionsAPI . registerResourceExtension (
component ,
"argoproj.io" ,
"Application" ,
"App Insights"
);
System-Level Extensions
Add new pages to the sidebar navigation.
registerSystemLevelExtension (
component : ExtensionComponent ,
title : string ,
path : string ,
icon ?: string
)
Example
(( window ) => {
const component = () => {
return React . createElement ( "div" , { style: { padding: "20px" } }, [
React . createElement ( "h1" , {}, "Custom Dashboard" ),
React . createElement ( "p" , {}, "Your custom content here" )
]);
};
window . extensionsAPI . registerSystemLevelExtension (
component ,
"Dashboard" , // Sidebar title
"/dashboard" , // URL path
"fa-chart-line" // FontAwesome icon
);
})( window );
Status Panel Extensions
Add custom items to the status bar at the top of the Application view.
registerStatusPanelExtension (
component : StatusPanelExtensionComponent ,
title : string ,
id : string ,
flyout ?: ExtensionComponent
)
Basic Example
(( window ) => {
const component = () => {
return React . createElement (
"div" ,
{ style: { padding: "10px" } },
"Status: OK"
);
};
window . extensionsAPI . registerStatusPanelExtension (
component ,
"Health Status" ,
"health_status"
);
})( window );
With Flyout Panel
(( window ) => {
const flyout = ( props ) => {
return React . createElement ( "div" , { style: { padding: "20px" } }, [
React . createElement ( "h3" , {}, "Detailed Health" ),
React . createElement ( "p" , {}, "All services operational" )
]);
};
const component = ( props ) => {
return React . createElement (
"div" ,
{
style: { padding: "10px" , cursor: "pointer" },
onClick : () => props . openFlyout ()
},
"View Details"
);
};
window . extensionsAPI . registerStatusPanelExtension (
component ,
"Health Details" ,
"health_details" ,
flyout
);
})( window );
Add buttons to the action bar at the top of the Application view.
registerTopBarActionMenuExt (
component : TopBarActionMenuExtComponent ,
title : string ,
id : string ,
flyout ?: ExtensionComponent ,
shouldDisplay ?: ( app ?: Application ) => boolean ,
iconClassName ?: string ,
isMiddle ?: boolean
)
Conditional Display Example
(( window ) => {
const shouldDisplay = ( app ) => {
// Only show for production apps
return app . metadata ?. labels ?.[ "environment" ] === "production" ;
};
const flyout = () => {
return React . createElement (
"div" ,
{ style: { padding: "20px" } },
"Production deployment controls"
);
};
const component = () => {
return React . createElement (
"div" ,
{},
"Deploy to Prod"
);
};
window . extensionsAPI . registerTopBarActionMenuExt (
component ,
"Production Deploy" ,
"prod_deploy" ,
flyout ,
shouldDisplay ,
"fa-rocket" ,
true // Position in middle section
);
})( window );
App View Extensions
Create custom application detail views alongside Tree, Pods, and Network views.
registerAppViewExtension (
component : ExtensionComponent ,
title : string ,
icon : string ,
shouldDisplay ?: ( app : Application ) => boolean
)
Example
(( window ) => {
const component = ( props ) => {
const { application , tree } = props ;
return React . createElement ( "div" , { style: { padding: "20px" } }, [
React . createElement ( "h2" , {}, "Custom View" ),
React . createElement ( "p" , {}, `Resources: ${ tree . nodes . length } ` )
]);
};
const shouldDisplay = ( app ) => {
// Only show for apps with specific annotation
return app . metadata ?. annotations ?.[ "custom-view" ] === "enabled" ;
};
window . extensionsAPI . registerAppViewExtension (
component ,
"Custom View" ,
"fa-cubes" ,
shouldDisplay
);
})( window );
Development Tips
Mount extensions during local development: kubectl create configmap extension-config \
--from-file=extension.js=./my-extension.js \
-n argocd
# Patch argocd-server deployment
kubectl patch deployment argocd-server -n argocd --type json \
-p= '[{
"op": "add",
"path": "/spec/template/spec/containers/0/volumeMounts/-",
"value": {
"name": "extensions",
"mountPath": "/tmp/extensions/my-ext"
}
}]'
Use browser DevTools: const component = ( props ) => {
console . log ( "Extension props:" , props );
console . log ( "Application:" , props . application );
return React . createElement ( "div" , {}, "Debug Extension" );
};
Wrap components with error boundaries: const component = ( props ) => {
try {
// Your component logic
return React . createElement ( "div" , {}, "Content" );
} catch ( error ) {
console . error ( "Extension error:" , error );
return React . createElement (
"div" ,
{ style: { color: "red" } },
"Extension failed to load"
);
}
};
Use webpack to bundle your extension: // webpack.config.js
module . exports = {
entry: './src/extension.jsx' ,
output: {
filename: 'extension.js' ,
path: __dirname + '/dist'
},
externals: {
react: 'React'
},
module: {
rules: [
{
test: / \. jsx ? $ / ,
use: 'babel-loader' ,
exclude: /node_modules/
}
]
}
};
TypeScript Support
For better type safety, define types for your extensions:
import { Application , State , ApplicationTree } from './argocd-types' ;
interface ResourceExtensionProps {
application : Application ;
resource : State ;
tree : ApplicationTree ;
}
const MyExtension : React . FC < ResourceExtensionProps > = ( props ) => {
const { application , resource , tree } = props ;
return < div > Typed Extension </ div > ;
};
declare global {
interface Window {
extensionsAPI : {
registerResourceExtension : (
component : React . ComponentType < ResourceExtensionProps >,
group : string ,
kind : string ,
title : string ,
opts ?: { icon ?: string }
) => void ;
// ... other methods
};
}
}
window . extensionsAPI . registerResourceExtension (
MyExtension ,
"apps" ,
"Deployment" ,
"My Extension"
);
Best Practices
Keep It Simple Extensions should be focused and lightweight. Complex logic should call external services.
Handle Loading States Show loading indicators for async operations to improve UX.
Error Boundaries Implement error handling to prevent extension failures from breaking the UI.
Responsive Design Ensure extensions work well on different screen sizes.
Security Considerations
Extension Security
Extensions run with full access to the application context
Only install extensions from trusted sources
Sanitize any user input before displaying
Don’t expose sensitive data in extensions
Review extension code before deployment
Example: Complete Extension
(( window ) => {
// Helper to safely access nested properties
const get = ( obj , path , defaultValue ) => {
const keys = path . split ( '.' );
let result = obj ;
for ( const key of keys ) {
if ( result && typeof result === 'object' && key in result ) {
result = result [ key ];
} else {
return defaultValue ;
}
}
return result ;
};
// Main component
const ResourceMetricsExtension = ( props ) => {
const { application , resource } = props ;
const [ metrics , setMetrics ] = React . useState ( null );
const [ loading , setLoading ] = React . useState ( true );
React . useEffect (() => {
// Fetch metrics from your monitoring system
fetch ( `/api/metrics/ ${ resource . name } ` )
. then ( res => res . json ())
. then ( data => {
setMetrics ( data );
setLoading ( false );
})
. catch ( err => {
console . error ( 'Failed to load metrics:' , err );
setLoading ( false );
});
}, [ resource . name ]);
if ( loading ) {
return React . createElement ( 'div' ,
{ style: { padding: '20px' , textAlign: 'center' } },
'Loading metrics...'
);
}
if ( ! metrics ) {
return React . createElement ( 'div' ,
{ style: { padding: '20px' , color: 'red' } },
'Failed to load metrics'
);
}
return React . createElement ( 'div' , { style: { padding: '20px' } }, [
React . createElement ( 'h3' , { key: 'title' }, 'Resource Metrics' ),
React . createElement ( 'p' , { key: 'cpu' }, `CPU: ${ metrics . cpu } ` ),
React . createElement ( 'p' , { key: 'memory' }, `Memory: ${ metrics . memory } ` ),
]);
};
// Register the extension
window . extensionsAPI . registerResourceExtension (
ResourceMetricsExtension ,
'apps' ,
'Deployment' ,
'Metrics' ,
{ icon: 'fa-chart-bar' }
);
})( window );
Next Steps
Resource Actions Add custom actions to resources
Custom Health Checks Define custom health checks
React Documentation Learn React fundamentals
Argo CD API View API type definitions