Layer's embeddable React component library provides an out of the box accounting experience. This project emphasizes composability and brandability, allowing you to
- Use full page layouts we've already tested and used or layout individual components however you choose.
- Brand components with your company colors, fonts, and more.
All you need to get started is sandbox development credentials - reach out here if you don't have those yet!
- Layer home: https://www.layerfi.com
- Documentation: https://docs.layerfi.com/introduction
- NPM: https://www.npmjs.com/package/@layerfi/components
npm install --save @layerfi/componentsor
yarn install @layerfi/componentsNest the components you want to use under the LayerProvider component. You should provide the component with a few props:
businessIdThe ID of the business whose information you're showing.businessAccessTokenTemporary authentication token scoped to this specific business. See the getting started guide for how to fetch these tokens on your backend.environment(Optional, defaults to "production") the Layer environment you're attempting to access [stagingorproduction]theme(Optional) to customize the look of componentsonError(Optional) to get notified about exceptions
import { LayerProvider } from "@layerfi/components";
...
<LayerProvider
businesId="..."
businessAccessToken="..."
environment={'staging'}
>
{...}
</LayerProvider>In development, it's often convenient to avoid fetching business-scoped tokens. You may find it more convenient to set your sandbox clientId and clientSecret directly, which will give you access to any business in your sandbox account.
import { LayerProvider } from "@layerfi/components";
...
<LayerProvider
businesId="..."
appId="..."
appSecret="..."
environment={'staging'}
>
{...}
</LayerProvider>Displays all accounts connected to Layer including embedded banking, Plaid, and custom accounts.
<LinkedAccounts
elevated={elevatedLinkedAccounts}
showLedgerBalance={showLedgerBalance}
/>Props:
asWidget: Minimized version of the componentelevated: Stylistic option to highlight componentshowLedgerBalance: Flag to enable or hide the current ledger balance corresponding to this external account. Useful for reconciliation.
The transaction categorization component handles displaying both categorized transactions and the workflow for categorizing new transactions as they are imported into Layer.
<BankTransactions asWidget />Optional properties and useBankTransactionsContext give more control over the transactions list:
/** Using props */
<BankTransactions
filters={{
amount: { min: 0, max: 100 },
}}
/>
/** Using hook */
const { setFilters } = useBankTransactionsContext()
setFilters({ amount: { min: 0, max: 10000 } })
<BankTransactions />import { ProfitAndLoss } from "@layerfi/components";
…
<ProfitAndLoss>
<ProfitAndLoss.Chart />
<ProfitAndLoss.Summaries />
<ProfitAndLoss.DatePicker />
<ProfitAndLoss.Table />
</ProfitAndLoss>import { ProfitAndLoss } from "@layerfi/components";
…
<ProfitAndLoss>
<ProfitAndLoss.Summaries />
</ProfitAndLoss>The P&L Summaries section supports several optional props:
actionable: When enabled, clicking the revenue & expense charts will open the P&L sidebar view.variants: Override the size of the component; supports a small and large version.
import { ProfitAndLoss } from "@layerfi/components";
…
<ProfitAndLoss>
<ProfitAndLoss.Table>
</ProfitAndLoss>The P&L table supports one optional prop:
lockExpandedforces the table to be fully expanded showing all rows
The Balance Sheet component displays an interactive table. A default date can be passed in via the effectiveDate prop.
The statement of cashflows component is an interactive table displaying cash changes broken out into investing, financing, and operational activities. It is shown calculated via the standard indirect method.
The chart of accounts gives direct read-and-write access into the double-entry general ledger underlying Layer's data. It exposes the list of accounts and enables users to create new accounts.
<ChartOfAccounts asWidget withDateControl withExpandAllButton />The following props are supported
asWidget: Displays a more compact version.withDateControl: Includes a date picker that determines the effective date of account balances displayed.withExpandAllButton: Optionally displays a button to expand and collapse all sub-account lists.
The Journal component displays the full history of journal entries and allows users to create journal entries by hand as well.
A sidebar expands to display details about the invoice.
<Journal />The follow props are supported:
asWidget: Compact version
You have a range of options for custom styling. In order of most simple to most control:
- Using the
themeattribute ofLayerProvider, - Setting CSS variables,
- Overwriting CSS classes.
The theme attribute allows you to set primary light and dark colors. The component will then use your primary colors to create a cohesive color palette across all components. We won't add any new colors, just different lightness of your brand colors for accents where helpful, e.g. hover states.
<LayerProvider
businessId="..."
environment="..."
theme={{
colors: {
// Base colors:
dark: { h: '28', s: '26%', l: '11%' },
light: { h: '52', s: '100%', l: '55%' },
},
}}
>
{...}
</LayerProvider>The colors can be defined in HSL, RGB or HEX:
// HSL:
colors: {
dark: { h: '28', s: '26%', l: '11%' },
light: { h: '52', s: '100%', l: '55%' },
},
// RGB:
colors: {
dark: { r: '36', g: '28', b: '21' },
light: { r: '255', g: '224', b: '27' },
},
// HEX:
colors: {
dark: { hex: '#241C15' },
light: { hex: '#FFE01B' },
},body .Layer__component {
--color-dark-h: 167;
--color-dark-s: 38%;
--color-dark-l: 15%;
--text-color-primary: #333;
}The full list of variables is listed in the variables.css file and contains:
// 1. BASE VARIABLES:
--color-black: #1a1a1a;
--color-white: white;
--color-info-success: hsl(145, 45%, 42%);
--color-info-success-bg: hsl(145, 59%, 86%);
--color-info-success-fg: hsl(145, 63%, 29%);
--color-info-warning: hsl(43, 100%, 44%);
--color-info-warning-bg: hsl(43, 100%, 84%);
--color-info-warning-fg: hsl(43, 88%, 26%);
--color-info-error: hsl(0 76% 50%);
--color-info-error-bg: hsl(0 83% 86%);
--color-info-error-fg: hsl(0 88% 32%);
--color-dark-h: 0;
--color-dark-s: 0%;
--color-dark-l: 7%;
--color-dark: hsl(var(--color-dark-h) var(--color-dark-s) var(--color-dark-l));
--color-light-h: 0;
--color-light-s: 0%;
--color-light-l: 90%;
--color-light: hsl(
var(--color-light-h) var(--color-light-s) var(--color-light-l)
);
--color-base-0: #fff;
--color-base-50: hsl(var(--color-dark-h) 1% 98%);
--color-base-100: hsl(var(--color-dark-h) 1% 96%);
--color-base-200: hsl(var(--color-dark-h) 1% 94%);
--color-base-300: hsl(var(--color-dark-h) 2% 92%);
--color-base-400: var(--color-light);
--color-base-500: hsl(var(--color-dark-h) 2% 53%);
--color-base-600: hsl(var(--color-dark-h) 7% 40%);
--color-base-700: hsl(var(--color-dark-h) 9% 27%);
--color-base-800: hsl(var(--color-dark-h) 12% 20%);
--color-base-900: var(--color-dark);
--color-base-1000: hsl(var(--color-dark-h) 20% 7%);
--max-component-width: 1408px;
--base-transparent-1: hsla(220, 43%, 11%, 0.01);
--base-transparent-2: hsla(220, 43%, 11%, 0.02);
--base-transparent-4: hsla(
var(--color-dark-h),
var(--color-dark-s),
var(--color-dark-l),
0.04
);
--base-transparent-5: hsla(
var(--color-dark-h),
var(--color-dark-s),
var(--color-dark-l),
0.05
);
--base-transparent-6: hsla(
var(--color-dark-h),
var(--color-dark-s),
var(--color-dark-l),
0.06
);
--base-transparent-8: hsla(
var(--color-dark-h),
var(--color-dark-s),
var(--color-dark-l),
0.08
);
--base-transparent-10: hsla(
var(--color-dark-h),
var(--color-dark-s),
var(--color-dark-l),
0.1
);
--base-transparent-12: hsla(
var(--color-dark-h),
var(--color-dark-s),
var(--color-dark-l),
0.12
);
// 2. BASE CUSTOMIZATION
--color-primary: var(--color-dark);
--color-accent: var(--color-light);
--color-secondary: var(--color-base);
--color-success: var(--color-info-success);
--color-danger: var(--color-info-error);
--color-danger-dark: hsl(349, 30%, 30%);
--color-danger-light: hsla(
var(--color-info-error-h),
var(--color-info-error-s),
var(--color-info-error-l),
0.4
);
--text-color-primary: var(--color-dark);
--text-color-secondary: var(--color-base-600);
--bg-element-focus: var(--color-base-50);
// 3. EXTENDED CUSTOMIZATION
--font-family: 'InterVariable', 'Inter', sans-serif;
--font-family-numeric: 'InterVariable', 'Inter', sans-serif;
--text-xs: 11px;
--text-sm: 12px;
--text-md: 14px;
--text-lg: 16px;
--text-heading: 24px; // 20px on mobile (max-width: 768px)
--text-heading-sm: 16px;
--font-weight-normal: 460;
--font-weight-bold: 540;
--spacing-4xs: 2px;
--spacing-3xs: 4px;
--spacing-2xs: 6px;
--spacing-xs: 8px;
--spacing-sm: 12px;
--spacing-md: 16px;
--spacing-lm: 20px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--spacing-2xl: 36px;
--spacing-3xl: 40px;
--spacing-5xl: 52px;
--bg-color: var(--color-white);
--border-color: var(--color-base-200);
--border-radius-4xs: 2px;
--border-radius-3xs: 4px;
--border-radius-2xs: 6px;
--border-radius-xs: 8px;
--border-radius-sm: 12px;
--border-radius: 16px;
--border-radius-5xl: 52px;
--text-color-transaction-credit: var(--color-info-success);
--text-color-varying-amount: var(--color-base-700);
--input-border-radius: var(--border-radius-2xs);
--input-font-size: var(--text-md);
--input-border-color: var(--color-base-300);
--input-placeholder-color: var(--color-base-500);
--label-color: var(--color-base-700);
--btn-font-size: var(--text-md);
--btn-border-radius: var(--border-radius-2xs);
--btn-color: var(--color-black);
--btn-bg-color: var(--color-white);
--btn-color-primary: var(--color-white);
--btn-bg-color-primary: var(--color-black);
--btn-color-hover: var(--color-white);
--btn-bg-color-hover: var(--color-primary);
--btn-color-icon: var(--color-black);
--btn-bg-color-icon: var(--color-base-50);
--btn-color-icon-hover: var(--color-primary);
--btn-bg-color-icon-hover: var(--color-accent);
--btn-secondary-color: var(--color-black);
--btn-secondary-bg-color: var(--color-white);
--badge-color: var(--color-base-900);
--badge-fg-color: var(--color-base-900);
--badge-bg-color: var(--color-base-400);
--badge-color-success: var(--color-info-success);
--badge-fg-color-success: var(--color-info-success-fg);
--badge-bg-color-success: var(--color-info-success-bg);
--badge-color-warning: var(--color-info-warning);
--badge-fg-color-warning: var(--color-info-warning-fg);
--badge-bg-color-warning: var(--color-info-warning-bg);
--badge-color-error: var(--color-info-error);
--badge-fg-color-error: var(--color-info-error-fg);
--badge-bg-color-error: var(--color-info-error-bg);
--badge-border-radius: var(--border-radius-sm);
--table-bg: var(--color-white);
--tooltip-color: var(--color-white);
--tooltip-bg-color: var(--color-base-800);
--chart-indicator-color: var(--color-base-700);For fine grained control, you can override specific classes. We recommend using your browser dev tools to find the component you want to restyle and override that specific class with a higher priority CSS rule.
body .Layer__table-cell-content {
background: white;
}
body .Layer__bank-transaction-row .Layer__table-cell-content {
background: gray;
}Use onError property on <LayerProvider /> to get notified about all exceptions:
<LayerProvider
businessId="..."
environment="..."
appId="..."
onError={err => console.log('My error callback', err)}
>
{...}
</LayerProvider>...or on the specific component:
<BankTransactions onError={e => console.log('My callback:', e)} />








