Laravel + Vue.js: how we build panels, CRMs and B2B apps
Author:
Paweł Matusiak
·
A custom CRM does not have to look like a 2010 relic. Laravel gives a solid backend and API, Vue.js a fast, modern UI. That is how you get systems teams actually use every day.
A ready-made CRM promises everything. In practice the team still keeps the truth in Excel because the fields do not fit and a report needs three exports. A dedicated Laravel + Vue.js panel reverses that: company process first, screens second. The UI is as simple as real work allows — not the other way round. We do not build a combine harvester with a thousand checkboxes. We build the path from enquiry to invoice, from booking to SMS, from a dispatch note to stock — and only then the ornaments.
It is the same stack we describe under technologies and in why Laravel for business apps. There is one backend. There can be several fronts: a Vue panel, a customer site, Android on the same API. We do not duplicate discount rules in three places. Below: the architecture, what we most often ship, how we guard roles and reports, and what the project looks like from the inside — with Git and 6 months of warranty at the end, not with code locked in someone else cloud.
An architecture that holds up
The Laravel backend exposes a REST API. Vue.js (Vite, Pinia) is the panel frontend: tables, filters, forms, dashboards. The same API can then serve an Android app, another system or a store on a separate domain. Validation and policies live on the server. Vue is not the source of truth about whether an invoice may be deleted — it is the interface. A hidden button is not a permission. That is A01 from OWASP in a panel.
On the server: Nginx, PHP 8, MySQL or PostgreSQL, Redis for cache, sessions and queues. Heavy work — PDF generation, mail, importing 20,000 SKUs — goes to a queue instead of blocking a click. The schedule (reminders, expired quotes, minimum stock) lives in code. The web panel logs in with a Sanctum session / cookie and CSRF; Android with a token you can revoke. Details in login and 2FA.
What we most often build in this stack
- CRM: customer records, opportunities, tasks, contact history, team permissions, notes without a field for everything.
- B2B panels: individual price lists, wholesale orders, invoices, delivery statuses, credit limits.
- Bookings and schedules: resources, staff, email/SMS notifications, deposits, calendar sync.
- Warehouse and dispatch notes: stock, locations, Android scanner, minimum alerts, movement history.
- Customer portals: tickets, documents, payments, job status — no office account on the customer side.
Roles that match the company
The owner sees margin. Sales sees their customers. Warehouse sees dispatches. Accounting sees documents. Laravel policies enforce this on the API, not only by hiding a button. The same API will reject a clever call from another browser. A shared account does not pass — otherwise there is no audit, no offboarding, no GDPR. The test a salesperson cannot download payroll is on CI, not on trust.
Reports that do not hurt the database
A live dashboard computes what it must: today orders, bottlenecks, alerts. Heavy daily/weekly reports land in Redis or an aggregate table, refreshed by a background job. The panel does not crawl on Friday afternoon when everyone clicks export to Excel. A large export needs a permission and often 2FA — it is not a footer link for everyone.
What the project looks like from the inside
- Process analysis and screen sketches — before we write the data model. We settle roles, retention, must-have integrations.
- A prototype of the key flow (e.g. from enquiry to invoice) on a test environment, on your examples.
- Staged development with a preview. A scope change — in writing, with an effect on date and price.
- Tests, permissions, backups, SSL, deployment on the client server. Sanctum, login limits, 2FA where needed.
- Git handover, documentation, 6 months of warranty, optional maintenance.
We do not start from a full dashboard with twelve charts. We start from the action without which the company stops. The rest arrives when the core is live. We say the same in pricing: Starter and Business on the site are a brochure and a small panel, not a catalogue CRM price. CRM / B2B with payments and stock is after a brief, in stages.
Why not Blade everywhere, and not a separate frontend combine
Simple sites and small panels can be Blade. When a team clicks eight hours a day through tables, filters and forms, a Vue SPA is faster to maintain and nicer to use — provided the API is the only place rules live. We do not add a separate BFF, GraphQL and three frontend environments on the first panel for 12 people. Vite, Pinia, table and form components, one design system. When a second app arrives (a customer portal), it uses the same endpoints and the same policies.
Queues and Redis are not a luxury. They are the difference between a panel that freezes on import and a panel that says accepted, we will let you know. Invoice PDFs, bulk mail, stock recalculation — workers. If someone promises the same on cheap shared hosting with no queue, ask what happens on the first Black Friday. We say that in analysis and we include a proper server or VPS; we do not hide it in a low price.
When we do not recommend this stack
If you need a blog and an offer — WordPress or Starter will be cheaper. If a SaaS eats 90% of the process and you can live with the rest in Excel — we do not force custom. If there is no process owner on your side, a workshop first, not a sprint. Laravel + Vue pays when someone logs in every day, when the rules are yours, when integrations do not fit a plugin and when the code should survive a vendor change. Then Git is not an extra. It is a condition.
Examples of the systems we build this way are in inspirations. If you see something close to your company — or you do not, and that is the point — describe the process you want to automate. A quote in 24 hours, no obligation. We will say whether this is a small panel in the Business range or a staged CRM with no catalogue price.
The data model before the pretty tables
The most expensive mistake in a custom CRM is to draw screens and invent columns while coding. First: what is a customer (company, person, site), what is an opportunity, what is an order, what is an accounting document. If wholesale sells on a tax id, sales thinks in people and the warehouse thinks in locations — that is three entities, not one name field. Laravel migrations and relations force that conversation in week one. Vue only shows it. A badly named table comes back as three months of work on the first margin report.
An import from old Excel is a stage of its own, not we will pull it in at the end. Tax-id deduplication, junk phones, three spellings of the same firm — that is product work, not a Sunday script. On test we show a conflict report before anything hits production. Otherwise the new panel starts messy and the team goes back to Excel, because at least the colours mean something there.
Queues, Redis and what the user should not wait for
A save order click should take a fraction of a second. Printing 200 PDFs, talking to a courier, recalculating a promotion across the price list — should not. A worker eats the job, the panel shows a status, mail arrives when it is ready. Redis holds SPA sessions, dictionary cache and locks so two imports do not overwrite stock. Without that, Black Friday or a stocktake ends in a timeout and a double document. This is not Enterprise for a corporation. It is the minimum if the panel is to survive a season.
Queue monitoring (a stuck worker, a growing backlog) is part of deployment, not an extra if there is budget. The 6-month warranty also covers job runners we delivered not going silent. It does not cover a new Slack channel that was not in scope. We write the line at acceptance so we do not guess later.
Tests, CI and what you do not see on a screenshot
A policy without a test dies on the first hotfix. CI runs permission tests, validation and the key flows (order, payment, password reset). We do not promise 100% coverage on an MVP — we promise that the scenario that hurts the business is green and that it does not sneak back after a table refactor. We test Vue where logic lives in a component (filters, form state), not where it would duplicate the server.
A test environment is in the project price, not a nice extra. You click there before any end customer sees it. Test data is anonymised if production holds personal data — otherwise the test box is a second dataset under GDPR. Production deploys are repeatable, not FTP on a Friday night. The same Git repository we hand over is the one the server boots from.
SPA, Pinia and where the front ends
Vue 3 + Vite builds the panel: HMR on test, code-split in production. Pinia holds UI state (filters, the user session, a form draft), not discount rules. Rules live in Laravel: services, policies, jobs. If the same discount must be computed in Vue, in Android and in cron — you will duplicate the bug. The API is the source of truth. Vue shows the Form Request 422 on the field; it does not guess. We do not use v-html on customer data — XSS in an accounting panel is worse than on a blog.
SPA routing does not replace authorisation. A Vue guard hides a menu; Sanctum + a policy rejects the request. We do not keep tokens for our own panel in localStorage (Sanctum docs: cookie session). Android gets a personal access token with revoke. We add API versioning (/api/v1) when a second client (mobile, a partner) cannot take a breaking change the same day as the panel. On the first CRM for 12 people we do not stand up GraphQL, a BFF and three design systems — too small a team, too wide a contract.
Idempotency, jobs and Friday at 4 pm
Saving an order is synchronous and short. Printing 200 PDFs, a large SKU import, talking to a courier, recalculating a promotion — ShouldQueue. Retry, backoff, failed_jobs, an alert when the backlog grows. An idempotency key on the payment webhook: the same payload does not book a second payment (OWASP 2025 A10). A Redis lock on stock import so two CSV files do not kill each other. Horizon or an equivalent queue view is in the deployment, not if there is budget. Without a worker, cheap shared hosting dies on the first stocktake.
Heavy reports are computed as a background aggregate, not a five-year SUM on every dashboard hit. Pagination and indexes on foreign keys are hygiene, not optimisation later. A large CSV export — a permission, a queue, often 2FA. Otherwise A01 (a one-click mass leak) and GDPR meet in a single download everything button.
An API contract with Android and with a human
The same REST, two clients. Kotlin does not get a mobile database. Offline in the field is designed only when there really is no LTE, with an explicit sync queue and conflicts (two dispatches of the same SKU). A barcode scanner is the camera + an endpoint, not a second ERP. Push — when a status must catch a courier, not so we have an app in the store. iOS is not in the Android line price; if it is needed, it is in the brief and the annex. Endpoint docs (OpenAPI or equivalent) go with Git so the next team does not guess from the network tab.
Stage acceptance: a brief scenario clicked on test, on your (anonymised) data. 6 months of warranty on the delivered scope, not on a new Slack channel. Pricing with no CRM catalogue; login in the project standard. Describe the flow from enquiry to a document — we will say whether this is a Business-small panel or a staged CRM.
Frequently asked questions
- How is a Laravel + Vue panel different from a ready CRM?
- A package has a thousand fields you use twelve of. A dedicated panel matches your process: roles, reports, integrations — no spreadsheet beside it. More at the start, cheaper when the fields already fail in month two.
- Does the Android app use the same backend?
- Yes. One Laravel API, two interfaces (Vue and Kotlin). You do not keep a separate mobile database. Sanctum tokens can be revoked.
- How long is a custom CRM?
- From a month to several months, in stages. We start from the key flow (enquiry to invoice), not a full dashboard. A yesterday deadline with floating scope costs more.
- Can I change vendor later?
- Yes. You get Git and docs. Laravel and Vue are common enough that another developer in Poland can read the code. 6 months of warranty on the delivered scope stays with us.
- Is this a token SPA?
- The web panel: a Sanctum session / cookie with CSRF, as the docs say. Mobile: tokens. We do not mix one bag for everything. Details in the 2FA and Sanctum article.
- How much does it cost?
- A small panel may fit the Business from PLN 6,000 order of magnitude only when the scope is truly small. CRM / B2B with integrations — after a brief, no catalogue price. Written up in the pricing article.
- Can I edit content without a developer?
- Yes, where content is content (pages, mail, statuses). Discount rules and permissions are not a CMS for everyone — on purpose, so an intern cannot open the margin of the whole wholesale book.
- What about performance on large tables?
- Pagination, indexes, background aggregates, queued exports. The dashboard does not compute five years of history on every visit. If someone promises everything live on one screen, ask about Friday at 4 pm.
Related service:
Custom B2B platforms and CRM
Describe your project