Documentation Index Fetch the complete documentation index at: https://mintlify.com/evidence-dev/evidence/llms.txt
Use this file to discover all available pages before exploring further.
Evidence supports custom Svelte components, allowing you to extend the built-in component library with your own interactive visualizations and UI elements.
Why Custom Components?
Custom components are useful when you need to:
Create specialized visualizations not available in the built-in library
Integrate third-party JavaScript libraries
Build reusable UI patterns specific to your organization
Implement custom business logic or calculations
Add advanced interactivity beyond standard inputs
Creating a Custom Component
Custom components are Svelte files (.svelte) placed in your project’s components directory.
Project Structure
my-evidence-project/
├── components/
│ ├── CustomChart.svelte
│ ├── MetricCard.svelte
│ └── DataFilter.svelte
├── pages/
│ └── index.md
└── sources/
Basic Component Example
Create components/MetricCard.svelte:
< script >
// Props that can be passed to the component
export let title = "Metric" ;
export let value ;
export let change ;
export let fmt = "num0" ;
// Determine color based on change
$ : changeColor = change >= 0 ? 'text-green-600' : 'text-red-600' ;
$ : changeIcon = change >= 0 ? '↑' : '↓' ;
</ script >
< div class = "metric-card" >
< h3 class = "text-sm font-medium text-gray-600" > { title } </ h3 >
< div class = "flex items-baseline gap-2" >
< span class = "text-3xl font-bold" >
{ value . toLocaleString () }
</ span >
{# if change !== undefined }
< span class = { changeColor } >
{ changeIcon } { Math . abs ( change ). toFixed ( 1 ) } %
</ span >
{/ if }
</ div >
</ div >
< style >
.metric-card {
padding : 1.5 rem ;
background : white ;
border-radius : 0.5 rem ;
border : 1 px solid #e5e7eb ;
}
</ style >
Using the Component
In your markdown pages:
```sql revenue_metrics
SELECT
SUM (revenue) as total_revenue,
12 . 5 as revenue_change_pct
FROM orders
## Working with Query Data
Custom components can accept and process query results:
### Component Accepting Data Prop
```svelte
<!-- components/SimpleTable.svelte -->
<script>
export let data = [];
export let columns = [];
// Auto-detect columns if not provided
$: displayColumns = columns.length > 0
? columns
: (data.length > 0 ? Object.keys(data[0]) : []);
</script>
<div class="overflow-x-auto">
<table>
<thead>
<tr>
{#each displayColumns as column}
<th>{column}</th>
{/each}
</tr>
</thead>
<tbody>
{#each data as row}
<tr>
{#each displayColumns as column}
<td>{row[column]}</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
<style>
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid #e5e7eb;
}
th {
font-weight: 600;
background-color: #f9fafb;
}
</style>
Usage
```sql orders
SELECT
order_id,
customer_name,
order_date,
total
FROM orders
LIMIT 10
## Integrating Third-Party Libraries
You can use npm packages in custom components.
### Installing Dependencies
First, install the package:
```bash
npm install d3-scale
Using the Library
<!-- components/ColorScale.svelte -->
< script >
import { scaleLinear } from 'd3-scale' ;
export let value ;
export let min = 0 ;
export let max = 100 ;
export let colors = [ '#fee' , '#f00' ];
$ : colorScale = scaleLinear ()
. domain ([ min , max ])
. range ( colors );
$ : backgroundColor = colorScale ( value );
</ script >
< div class = "color-cell" style = "background-color: { backgroundColor } " >
{ value }
</ div >
< style >
.color-cell {
padding : 0.5 rem 1 rem ;
border-radius : 0.25 rem ;
font-weight : 500 ;
}
</ style >
Interactive Components
Create components with internal state and interactions:
<!-- components/ExpandableCard.svelte -->
< script >
export let title ;
export let data ;
let expanded = false ;
function toggle () {
expanded = ! expanded ;
}
</ script >
< div class = "card" >
< button class = "header" on : click = { toggle } >
< h3 > { title } </ h3 >
< span class = "icon" > { expanded ? '−' : '+' } </ span >
</ button >
{# if expanded }
< div class = "content" >
< slot />
</ div >
{/ if }
</ div >
< style >
.card {
border : 1 px solid #e5e7eb ;
border-radius : 0.5 rem ;
overflow : hidden ;
}
.header {
width : 100 % ;
display : flex ;
justify-content : space-between ;
align-items : center ;
padding : 1 rem ;
background : white ;
border : none ;
cursor : pointer ;
}
.header:hover {
background : #f9fafb ;
}
.content {
padding : 1 rem ;
border-top : 1 px solid #e5e7eb ;
}
</ style >
Using Slots
Slots allow you to pass content into components:
<!-- components/Panel.svelte -->
< script >
export let title ;
export let variant = 'default' ;
</ script >
< div class = "panel panel- { variant } " >
< div class = "panel-header" >
< h2 > { title } </ h2 >
{# if $$ slots . actions }
< div class = "panel-actions" >
< slot name = "actions" />
</ div >
{/ if }
</ div >
< div class = "panel-body" >
< slot />
</ div >
{# if $$ slots . footer }
< div class = "panel-footer" >
< slot name = "footer" />
</ div >
{/ if }
</ div >
Usage with Slots
< Panel title = "Revenue Analysis" variant = "primary" >
< div slot = "actions" >
< button > Export </ button >
</ div >
<!-- Default slot content -->
< BarChart data = {revenue} x = "month" y = "total" />
< div slot = "footer" >
Last updated: January 15, 2024
</ div >
</ Panel >
Component Props Validation
Validate and provide defaults for props:
< script >
export let data ;
export let title = "Chart" ;
export let width = 600 ;
export let height = 400 ;
export let colorScheme = 'default' ;
// Validation
$ : if ( ! data || data . length === 0 ) {
console . warn ( 'No data provided to component' );
}
$ : if ( width < 200 || height < 200 ) {
console . warn ( 'Chart dimensions may be too small' );
}
// Computed values
$ : aspectRatio = width / height ;
</ script >
Accessing Evidence Context
Evidence provides context that components can access:
< script >
import { getContext } from 'svelte' ;
// Access Evidence theme
const { theme } = getContext ( 'evidence' );
export let data ;
$ : primaryColor = $ theme . colors . primary ;
</ script >
< div style = "color: { primaryColor } " >
<!-- Component content -->
</ div >
Exporting Components as a Package
Create reusable component libraries:
Package Structure
my-components/
├── package.json
├── src/
│ ├── index.js
│ ├── MetricCard.svelte
│ └── CustomChart.svelte
└── README.md
package.json
{
"name" : "@myorg/evidence-components" ,
"version" : "1.0.0" ,
"svelte" : "src/index.js" ,
"exports" : {
"." : "./src/index.js"
},
"peerDependencies" : {
"svelte" : "^4.0.0"
}
}
src/index.js
export { default as MetricCard } from './MetricCard.svelte' ;
export { default as CustomChart } from './CustomChart.svelte' ;
Best Practices
Props over hardcoding - Make components flexible with props
Provide defaults - Set sensible default values for optional props
Validate inputs - Check for required props and valid values
Use TypeScript - Add type safety with TypeScript
Document components - Include JSDoc comments for props
Keep it simple - Start simple, add complexity as needed
Responsive design - Make components work on all screen sizes
Accessibility - Follow ARIA guidelines for interactive components
TypeScript Support
Use TypeScript for type-safe components:
< script lang = "ts" >
export let data : Array <{ date : string , value : number }>;
export let title : string = "Chart" ;
export let showLegend : boolean = true ;
interface ChartConfig {
width : number ;
height : number ;
margin : { top : number ; right : number ; bottom : number ; left : number };
}
const config : ChartConfig = {
width: 600 ,
height: 400 ,
margin: { top: 20 , right: 20 , bottom: 30 , left: 40 }
};
</ script >
Debugging Components
Use Svelte’s reactive debugging:
< script >
export let data ;
// Log when data changes
$ : console . log ( 'Data updated:' , data );
// Reactive statement with multiple dependencies
$ : {
console . log ( 'Recalculating chart with:' , {
dataLength: data ?. length ,
width ,
height
});
}
</ script >
Example: Custom Chart Component
Complete example of a custom chart component:
<!-- components/ProgressChart.svelte -->
< script >
export let data ;
export let targetColumn = 'target' ;
export let actualColumn = 'actual' ;
export let nameColumn = 'name' ;
export let height = 400 ;
$ : maxValue = Math . max (
... data . map ( d => Math . max ( d [ targetColumn ], d [ actualColumn ]))
);
function getPercentage ( value ) {
return ( value / maxValue ) * 100 ;
}
function getColor ( actual , target ) {
const pct = ( actual / target ) * 100 ;
if ( pct >= 100 ) return '#10b981' ;
if ( pct >= 75 ) return '#f59e0b' ;
return '#ef4444' ;
}
</ script >
< div class = "progress-chart" style = "height: { height } px" >
{# each data as item }
< div class = "progress-row" >
< div class = "label" > { item [ nameColumn ] } </ div >
< div class = "bars" >
< div class = "bar-container" >
< div
class = "bar target"
style = "width: { getPercentage ( item [ targetColumn ]) } %"
>
< span class = "bar-label" > Target: { item [ targetColumn ] } </ span >
</ div >
< div
class = "bar actual"
style = "
width: { getPercentage ( item [ actualColumn ]) } %;
background-color: { getColor ( item [ actualColumn ], item [ targetColumn ]) }
"
>
< span class = "bar-label" > Actual: { item [ actualColumn ] } </ span >
</ div >
</ div >
</ div >
</ div >
{/ each }
</ div >
< style >
.progress-chart {
display : flex ;
flex-direction : column ;
gap : 1 rem ;
padding : 1 rem ;
}
.progress-row {
display : flex ;
gap : 1 rem ;
align-items : center ;
}
.label {
flex : 0 0 150 px ;
font-weight : 500 ;
}
.bars {
flex : 1 ;
}
.bar-container {
position : relative ;
height : 40 px ;
}
.bar {
position : absolute ;
height : 20 px ;
border-radius : 4 px ;
transition : width 0.3 s ;
}
.bar.target {
top : 0 ;
background-color : #e5e7eb ;
}
.bar.actual {
top : 22 px ;
}
.bar-label {
padding : 0 0.5 rem ;
font-size : 0.75 rem ;
line-height : 20 px ;
white-space : nowrap ;
}
</ style >
Next Steps
Svelte Documentation Learn more about Svelte
Component Examples Browse built-in components