|

Crea Calendarios con Laravel | 2026

Para crear un calendario en Laravel, necesitás un modelo de eventos, una vista Blade que renderice los días del mes usando Carbon, y opcionalmente Livewire para que sea interactivo sin reloads. Usá Carbon para calcular el inicio y fin de cada mes, almacená los eventos en una tabla con fechas, y consultá la base de datos filtrando por rango de fechas. Con laravel 12 las funciones de manipulación de fechas mejoraron bastante (lanzado 24 de febrero de 2025).

En 30 segundos

  • Usá una tabla de eventos con campos fecha_inicio, fecha_fin, usuario_id
  • Renderizá el calendario en cuadrícula HTML con loops Blade y Carbon
  • Agregá Livewire para navegación entre meses sin reload
  • Filtrá con whereDate() y whereBetween() de Eloquent
  • Optimizá con índices en columnas de fecha y caché por mes

Qué es un calendario con Laravel

Un calendario en Laravel es una interfaz que muestra eventos organizados por mes, donde cada día es clickeable para ver o crear eventos. Funciona consultando una base de datos de eventos filtrados por rango de fechas, usando Carbon para calcular qué días caen en cada semana. Es ideal para aplicaciones de reservas (hoteles, peluquerías), citas médicas, gestión de tareas o eventos comunitarios.

Configuración inicial del proyecto

calendario con laravel diagrama explicativo

Lo primero es levantar un proyecto nuevo. Abrís la terminal, navegás a donde querés que viva el proyecto, y corrés:

  • composer create-project laravel/laravel calendario-app
  • cd calendario-app
  • php artisan serve

Eso te crea la estructura estándar. Ahora necesitás las dependencias para el calendario. Si querés Livewire (altamente recomendable para no recargar la página), lo instalás con composer require livewire/livewire. Carbon ya viene incluido con Laravel.

Configurá tu base de datos en el archivo .env (puede ser SQLite, MySQL, o lo que uses). Luego corrés las migraciones base:

  • php artisan migrate

Diseñar la base de datos: modelo de eventos

Creá un modelo Event y su migración de una vez:

  • php artisan make:model Event -m

En el archivo de migración que se genera, definís la tabla así:

  • id (bigIncrements)
  • user_id (foreignId, apunta a usuarios)
  • titulo (string)
  • descripcion (text, nullable)
  • fecha_inicio (datetime)
  • fecha_fin (datetime, nullable)
  • created_at y updated_at (timestamps)

Agregá índices en fecha_inicio y fecha_fin para que las consultas de rangos sean rápidas. En el modelo Event, definís la relación con User: Esto se conecta con lo que analizamos en ejecutar agentes sin depender de APIs.

  • public function user() { return $this->belongsTo(User::class); }

Y una relación inversa en User para que puedas hacer $user->events. Eso te permite cargar todos los eventos de un usuario con una query eficiente.

Renderizar el calendario: vista HTML

Acá viene lo piola. Necesitás una vista Blade que dibuje un calendario en cuadrícula (7 columnas, 4-6 filas). El truco es usar Carbon para calcular qué día cae el primer del mes y cuántos días tiene.

Creá una vista, llamémosla resources/views/calendar.blade.php. En el controller que la renderiza, pasás la data así:

  • $now = Carbon::now();
  • $firstDay = $now->copy()->startOfMonth();
  • $lastDay = $now->copy()->endOfMonth();
  • $events = Event::whereBetween(‘fecha_inicio’, [$firstDay, $lastDay])->get();

Después en la vista, usás loops para armar las filas y columnas. Fijate que la mayoría de calendarios comienzan el domingo o el lunes (depende de tu localización). Podés iterar sobre cada día del mes y verificar si ese día tiene eventos asociados. Si los tiene, marcás ese día con una clase CSS diferente o un badge.

El problema que muchos se topan acá (y es medio tramposo) es que si el mes empieza un jueves, los primeros 3 días de la fila están vacíos. Tenés que llenarlos con números del mes anterior en gris o blanco. Carbon te facilita esto con daysInMonth y dayOfWeek.

Agregar interactividad con Livewire

Con solo HTML y Blade ya tenés un calendario funcional, pero si querés que navegue entre meses sin reload, Livewire es tu amigo. Creás un componente: Relacionado: consideraciones de seguridad y privacidad.

  • php artisan make:livewire CalendarComponent

En la clase del componente, almacenás la fecha actual del calendario como propiedad pública. Cuando el usuario clickea “siguiente mes”, una acción Livewire modifica esa propiedad, Blade se re-renderiza, y todo sucede sin recargar. Livewire maneja el JavaScript y la comunicación por detrás (no necesitás escribir una sola línea de AJAX).

La clase que genera Livewire tiene este aspecto básico:

  • public $currentDate;
  • public function mount() { $this->currentDate = Carbon::now(); }
  • public function nextMonth() { $this->currentDate->addMonth(); }
  • public function previousMonth() { $this->currentDate->subMonth(); }

Y en la vista asociada, botones que llaman #[wire:click]=”nextMonth” y #[wire:click]=”previousMonth”. Así, con tres líneas de código cada acción, tenés un calendario que navega sin recargas.

Funcionalidades avanzadas: búsqueda y filtros

Una vez que el calendario base anda, probablemente querés filtrar eventos por rango de fechas personalizado, o buscar por título. Eloquent ya viene armado para esto.

Para filtrar entre dos fechas, usás whereBetween:

  • $events = Event::whereBetween(‘fecha_inicio’, [$startDate, $endDate])->get();

Para buscar por título:

  • $events = Event::where(‘titulo’, ‘like’, ‘%’ . $search . ‘%’)->get();

Lo mejor es encapsular estas consultas en query scopes en el modelo Event. Un scope permite reutilizar filtros de forma limpia:

  • public function scopeByDateRange($query, $start, $end) { return $query->whereBetween(‘fecha_inicio’, [$start, $end]); }

Ahora en el controller podés hacer Event::byDateRange($start, $end)->get() y queda mucho más legible. Si tenés muchos eventos, agregá paginación con ->paginate(15) para no cargar todo de una vez.

Consideraciones de rendimiento y zonas horarias

Acá es donde la mayoría de proyectos se rompe en producción, así que prestá atención. Primero, índices: si hacés consultas recurrentes por rango de fechas, poné un índice compuesto en (user_id, fecha_inicio) o lo que filtres más. Sobre eso hablamos en herramientas de IA disponibles hoy.

Segundo, timezones (esto es medio viscoso). Laravel tiene un config ‘app.timezone’ en config/app.php. Si tu servidor está en UTC pero tus usuarios en Argentina (UTC-3), necesitás convertir. Carbon maneja esto bien, pero tenés que ser consistente. Almacená todo en UTC en la base de datos, y convertí a hora local en la vista.

Tercero, caché. Si tenés un calendario que no cambia frecuentemente, cacheá los eventos del mes completo con Redis o archivos. Algo como Cache::remember(‘events_’ . $month, 60 minutes, function() { … }) te ahorra queries innecesarias.

Cuarto, evitá queries N+1. Esto pasa cuando cargás eventos y luego iterás sobre ellos buscando datos del usuario relacionado, lo que genera una query por evento. Solucionalo con eager loading: Event::with(‘user’)->get().

Quinto, formatos de fecha. Carbon formatter es poderoso pero los idiomas localizados (es_AR para Argentina) importan. Configurá el locale en config/app.php para que los nombres de meses salgan en español y no en inglés.

Errores comunes que evitar

No validar las fechas

Alguien carga un evento con fecha_fin anterior a fecha_inicio y tu calendario explota (o muestra un evento imposible). Agregá una validación en el modelo o en el request FormRequest. Carbon permite comparar fechas fácilmente: if ($this->fecha_fin < $this->fecha_inicio) abort(422).

Confundir startOfMonth con firstDayOfMonth

Carbon.startOfMonth() te da las 00:00:00 del primer día. firstDayOfMonth() también, pero en contextos diferentes. Usá siempre startOfMonth() y endOfMonth() para consultas de rangos porque son semánticamente claros. Para más detalles técnicos, mirá elegir entre plataformas de desarrollo.

Cargar eventos sin filtrar por usuario

Si tu calendario muestra eventos de TODOS los usuarios (bueno, si tu app es multiusuario), es un security hole. Siempre filtrá por auth()->user()->id. Event::where(‘user_id’, auth()->id())->…, no Event::all().

No usar timestamps en eventos de duración

Un evento de múltiples días comienza un lunes a las 09:00 y termina un viernes a las 17:00. Si solo almacenás fechas sin horas, tu evento de “lunes a viernes” se corre a medianoche y no renderiza bien. Usá datetime (fecha + hora), no date.

Preguntas Frecuentes

¿Cómo crear un calendario con Laravel si recién estoy aprendiendo?

Empezá sin Livewire. Solo HTML, Blade y Carbon. Hace un modelo Event, una migration, un controller que cargue los eventos del mes actual, y una vista con loops para armar las filas. Cuando lo tengas andando, agregá Livewire después. No necesitás nada del otro mundo: si podés entender foreach en Blade y Carbon basics, está cubierto.

¿Puedo usar Livewire para un calendario en Laravel?

Totalmente. Livewire es ideal porque te permite actualizar el mes mostrado sin recargar la página. Es básicamente un wrapper reactivo sobre Blade, así que escribís componentes normales de Laravel pero con reactivity gratis. Si querés más control, siempre podés agregar AlpineJS o hasta Vue, pero Livewire alcanza para la mayoría de casos.

¿Qué librería uso para calendarios en Laravel?

No necesitás una librería especializada. Laravel ya tiene todo integrado: Carbon para manipular fechas, Eloquent para queries, Blade para renderizar. Si querés algo con widgets visuales más sofisticados (drag-drop de eventos, repeticiones, time slots), ahí sí mirá librerías como FullCalendar (hay un wrapper para Laravel), pero para casos de uso simples (mostrar eventos, navegar meses), código a mano es más limpio y educativo.

¿Cómo filtro eventos entre dos fechas con Eloquent?

Usás whereBetween(‘columna_fecha’, [$fecha_inicio, $fecha_fin]). Ejemplo: Event::whereBetween(‘fecha_inicio’, [Carbon::now()->startOfMonth(), Carbon::now()->endOfMonth()])->get(). Acordate que whereBetween es inclusivo en ambos extremos.

¿Cómo muestro eventos en un calendario con Laravel?

Cargás los eventos del mes desde el controller, los pasás a la vista, e iterás sobre los días del calendario verificando si cada día existe en la colección de eventos. Podés agrupar los eventos por fecha para que la búsqueda sea más rápida: $events->groupBy(‘fecha_inicio->format(Y-m-d)’). Después en Blade, para cada día buscás si existe en el grupo.

Conclusión

Un calendario funcional en Laravel no es complicado si entendés los pilares: un modelo Event, una tabla con fechas, Carbon para manipular rangos, y Blade para renderizar. Podés empezar simple (HTML + Blade sin JS) y crecer hacia Livewire o Vue cuando necesites más reactividad. La mayoría de las trampas vienen de not validar fechas, confundir timezones, y cargar queries ineficientes. Si usás eager loading, índices, y filtros por usuario, tu calendario escala sin problemas. Y si necesitás alojar la aplicación, plataformas como donweb.com ofrecen hosting con PHP 8.2+ y MySQL 8 que funcionan perfecto para Laravel 12.

Fuentes

Similar Posts