Development of Custom CRM Cards in Bitrix24
The standard deal card in Bitrix24 is a universal interface for all industries at once. As a result, an equipment sales manager sees the same fields as a rental manager. Fields aren't grouped by meaning, calculated values are absent, conditional logic for display is missing. A custom card solves a specific task: show the manager exactly what they need at the current work stage.
Two Levels of Customization
Bitrix24 offers two ways to change a CRM entity card, and they differ fundamentally in depth.
Level 1: Customization through the interface. In a deal card (contact, smart-process) click "Configure card" → "Configure fields and sections". Here you can:
- Rename sections
- Drag fields between sections
- Hide unnecessary fields
- Set field requirements by stages (for deals)
This is free and covers ~40% of requests. But you can't: add calculated fields, conditional logic, custom widgets, data from external systems.
Level 2: Development through REST Placement API. This is embedding arbitrary HTML/JS content into specific card zones. Here's where real customization begins.
Placement API: Embedding Points
A CRM entity card contains several zones (placements) where you can embed custom content:
-
CRM_DEAL_DETAIL_TAB— tab in deal card. Full area for any UI. -
CRM_DEAL_DETAIL_ACTIVITY— block in timeline. Good for custom actions. -
CRM_CONTACT_DETAIL_TAB,CRM_COMPANY_DETAIL_TAB— same for contacts and companies. -
CRM_DYNAMIC_ITEM_DETAIL_TAB— tab in smart-process card.
Placement registration via REST:
BX24.callMethod('placement.bind', {
PLACEMENT: 'CRM_DEAL_DETAIL_TAB',
HANDLER: 'https://your-app.com/deal-tab.html',
TITLE: 'Margin Calculator'
});
After registration, a new tab appears in the card. When opened, Bitrix24 loads the HANDLER in an iframe and passes context: ENTITY_ID (deal ID), AUTH_ID, REFRESH_ID.
Deep Dive: Custom Tab with Data
Consider a typical use case — a "Finance" tab in a deal card showing: sum by product items, cost from external 1C, margin, payment history.
Step 1: Get deal data
BX24.init(function() {
var dealId = BX24.placement.info().options.ID;
BX24.callBatch({
deal: ['crm.deal.get', {id: dealId}],
products: ['crm.deal.productrows.get', {id: dealId}]
}, function(result) {
var deal = result.deal.data();
var products = result.products.data();
renderFinanceTab(deal, products);
});
});
Step 2: Request cost from external API. The tab makes a request to your backend, passing product SKUs. The backend proxies the request to 1C or ERP and returns the cost.
Step 3: Render. Calculate margin on the client and display the table. For visual match use CSS variables from B24 or the @bitrix24/b24-ui library.
Conditional Field Display Logic
Frequent request: hide/show fields depending on another field's value. For example, "Reason for Refusal" visible only at "Failed" stage. Standard functionality allows making a field required by stage, but not hiding it.
Solution — custom handler through placement CRM_DEAL_DETAIL_TAB or embedding JS code through custom field type. In the on-premise version you can modify template of component bitrix:crm.deal.detail with JS logic in script.js.
For cloud version the only clean way is placement with full redraw of needed field section. This means: you fetch data via REST, render fields with conditional logic in your iframe, and on save send changes back via crm.deal.update.
Widgets in Timeline
Placement CRM_*_DETAIL_ACTIVITY embeds a block right in the activity feed of the card. Suitable for:
- Displaying delivery status (data from delivery company API)
- Showing client balance from accounting system
- Displaying latest tickets from helpdesk
The widget refreshes each time the card opens. For caching use BX24.appOption — app storage on B24 side.
Performance
Iframe tabs load on activation (click the tab). For heavy tabs this is normal — user doesn't wait for loading what they didn't open. But if you embed a widget in the main card area — it loads immediately. Each REST request from iframe — a network round-trip. Use BX24.callBatch to combine requests: one batch instead of five sequential calls.
| Card Element | Customization Method | Timeframe |
|---|---|---|
| Regroup fields | UI Configuration | 1–2 hours |
| Required by stage | UI Configuration | 30 minutes |
| Custom tab with external data | REST Placement | 3–5 days |
| Conditional field logic (cloud) | REST Placement + custom render | 5–7 days |
| Timeline widget | REST Placement Activity | 2–3 days |
Cloud Version Limitations
In the cloud you can't modify standard card sections — only add new tabs and widgets. You can't remove standard fields from main view (only hide them through card settings). For full control over card interface — on-premise version with overriding template of component crm.deal.detail.

