Frontend development using React for 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    745
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

React Frontend Development for 1C-Bitrix

Using React alongside 1C-Bitrix raises an obvious question: why React when Bitrix has its own components? The answer is pragmatic: when page interactivity requirements go beyond jQuery widgets and the frontend team already works with React and TypeScript, dragging in the old Bitrix stack no longer makes sense.

React Integration Patterns with Bitrix

Three main scenarios for using React in a Bitrix project:

1. Embedded widgets. A React component is mounted on a specific DOM element inside a Bitrix template. Best suited for complex forms, interactive filters, and custom UI components.

2. SPA page inside Bitrix. All page content is rendered by React; Bitrix serves only as a container (header, footer, navigation) and a data source through an API.

3. Full headless. The React application lives separately; Bitrix is API only. The cleanest approach, but requires the most investment.

Project Setup

Project structure for a React frontend inside Bitrix:

/local/
  /js/
    /src/                   # React source files
      /components/
      /hooks/
      /api/                 # Bitrix API layer
        bitrix.ts
        catalog.ts
        cart.ts
      /store/               # Zustand/Redux
    vite.config.ts
    package.json
    tsconfig.json
  /templates/
    /main/                  # Bitrix site template

It is critical to keep React source files in /local/js/src/, not in public/ — this prevents them from being directly web-accessible and ensures they go through the build tool.

Typed Bitrix API Client

Instead of raw fetch calls, create a typed API client:

// /local/js/src/api/bitrix.ts
interface BitrixResponse<T> {
    result: T;
    total?: number;
    error?: string;
}

class BitrixApiClient {
    private baseUrl: string;
    private sessionId: string;

    constructor() {
        this.baseUrl = '/local/ajax/api.php';
        // Get sessid from Bitrix global variable
        this.sessionId = (window as any).BX?.bitrix_sessid?.() || '';
    }

    async get<T>(action: string, params: Record<string, unknown> = {}): Promise<T> {
        const url = new URL(this.baseUrl, window.location.origin);
        url.searchParams.set('action', action);
        Object.entries(params).forEach(([k, v]) =>
            url.searchParams.set(k, String(v)));

        const response = await fetch(url.toString(), {
            headers: { 'X-Bitrix-Csrf-Token': this.sessionId },
        });

        const data: BitrixResponse<T> = await response.json();
        if (data.error) throw new Error(data.error);
        return data.result;
    }

    async post<T>(action: string, body: Record<string, unknown>): Promise<T> {
        const formData = new FormData();
        formData.append('action', action);
        formData.append('sessid', this.sessionId);
        Object.entries(body).forEach(([k, v]) =>
            formData.append(k, String(v)));

        const response = await fetch(this.baseUrl, {
            method: 'POST',
            body: formData,
        });

        const data: BitrixResponse<T> = await response.json();
        if (data.error) throw new Error(data.error);
        return data.result;
    }
}

export const bitrixApi = new BitrixApiClient();

React Query for Data Fetching

React Query (TanStack Query) is the standard choice for working with server data in React. Integration with Bitrix:

// /local/js/src/api/catalog.ts
import { useQuery } from '@tanstack/react-query';
import { bitrixApi } from './bitrix';

interface CatalogItem {
    id: number;
    name: string;
    price: number;
    quantity: number;
    previewPicture: string;
}

export function useCatalogItems(sectionId: number, page: number) {
    return useQuery({
        queryKey: ['catalog', sectionId, page],
        queryFn: () => bitrixApi.get<CatalogItem[]>('catalog.list', {
            section_id: sectionId,
            page,
            limit: 24,
        }),
        staleTime: 5 * 60 * 1000, // cache for 5 minutes
    });
}

// In the component:
function CatalogSection({ sectionId }: { sectionId: number }) {
    const [page, setPage] = useState(1);
    const { data, isLoading, error } = useCatalogItems(sectionId, page);

    if (isLoading) return <CatalogSkeleton />;
    if (error) return <ErrorMessage error={error} />;

    return (
        <div className="catalog-grid">
            {data?.map(item => <ProductCard key={item.id} item={item} />)}
            <Pagination page={page} onChange={setPage} />
        </div>
    );
}

Integration with Bitrix Authentication

Authentication is a specific challenge with React + Bitrix. The standard approach: Bitrix manages the session and authentication, React checks the current state through an API:

// Hook for checking authentication state
export function useAuth() {
    return useQuery({
        queryKey: ['auth'],
        queryFn: () => bitrixApi.get<{ isAuthorized: boolean; userId?: number }>('user.current'),
        staleTime: Infinity, // do not refetch unnecessarily
    });
}

For actions that require authentication (adding to cart, placing an order), redirect unauthenticated users to the standard Bitrix login page — do not build your own unless there are specific requirements.

Testing

Components are tested with Vitest + Testing Library, independently of Bitrix:

// Mock the API for tests
vi.mock('../api/bitrix', () => ({
    bitrixApi: {
        get: vi.fn().mockResolvedValue([
            { id: 1, name: 'Product 1', price: 1000 }
        ]),
    }
}));

test('renders catalog item list', async () => {
    render(<CatalogSection sectionId={5} />);
    expect(await screen.findByText('Product 1')).toBeInTheDocument();
});

A React frontend in a Bitrix project works well when the rules are clear: Bitrix owns the data and business logic, React owns the UI and user interaction. Mixing logic between Bitrix templates and React components simultaneously leads to unmaintainable code within six months.