diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6a837d317ce16a4e5835f5963e0dfb85234ceca..3d32a5fffda8341ad4b38401133016a7dbe3340e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Add indicator for active filters [@cedricwillekens](https://gitlab.ewi.tudelft.nl/cedricwilleken)
 - Allow users to view and create new shared editions. [@cedricwillekens](https://gitlab.ewi.tudelft.nl/cedricwilleken)
 - Add room images for student requests [@cedricwilleken](https://gitlab.ewi.tudelft.nl/cedricwilleken)
+- Hybrid Labs are now supported regardless of what the direction of the lab is. [@hpage](https://gitlab.ewi.tudelft.nl/hpage)
 
 ### Changed
 - Redirect students to their request when their are being processed when accessing the lab page. [@cedricwilleken](https://gitlab.ewi.tudelft.nl/cedricwilleken)
diff --git a/src/main/java/nl/tudelft/queue/dto/create/QueueSessionCreateDTO.java b/src/main/java/nl/tudelft/queue/dto/create/QueueSessionCreateDTO.java
index 2ac52e1a5efd9ef78d0c80b4ddbeaaa5af6beaef..480c00ca34b92c714e550127577d66deb9bad5b2 100644
--- a/src/main/java/nl/tudelft/queue/dto/create/QueueSessionCreateDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/create/QueueSessionCreateDTO.java
@@ -44,7 +44,7 @@ public abstract class QueueSessionCreateDTO<D extends QueueSession<?>> extends C
 	private Set<Long> modules = new HashSet<>();
 
 	@Builder.Default
-	private Set<Long> rooms = new HashSet<>();
+	protected Set<Long> rooms = new HashSet<>();
 
 	@Builder.Default
 	private LabRequestConstraintsCreateDTO constraints = new LabRequestConstraintsCreateDTO();
@@ -74,7 +74,6 @@ public abstract class QueueSessionCreateDTO<D extends QueueSession<?>> extends C
 
 		nonEmpty("modules", modules);
 
-		nonEmpty("rooms", rooms);
 	}
 
 	@Override
diff --git a/src/main/java/nl/tudelft/queue/dto/create/labs/CapacitySessionCreateDTO.java b/src/main/java/nl/tudelft/queue/dto/create/labs/CapacitySessionCreateDTO.java
index 87d9c921e7785034fa9f3bc2c454f194501eed9e..62e7df05b95b0224cab86147a31e98bf2997ec89 100644
--- a/src/main/java/nl/tudelft/queue/dto/create/labs/CapacitySessionCreateDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/create/labs/CapacitySessionCreateDTO.java
@@ -55,6 +55,8 @@ public class CapacitySessionCreateDTO extends QueueSessionCreateDTO<CapacitySess
 	public void validate() {
 		super.validate();
 
+		nonEmpty("rooms", rooms);
+
 		if (capacitySessionConfig.getEnrolmentClosesAt()
 				.isBefore(capacitySessionConfig.getEnrolmentOpensAt())) {
 			errors.rejectValue("capacitySessionConfig.enrolmentClosesAt",
diff --git a/src/main/java/nl/tudelft/queue/dto/create/labs/LabCreateDTO.java b/src/main/java/nl/tudelft/queue/dto/create/labs/LabCreateDTO.java
index 1845586e73a64f611464be03d3439474d0cecb26..8ddd9ea4af5bea4e65a2d09390ca3d84bac73ff4 100644
--- a/src/main/java/nl/tudelft/queue/dto/create/labs/LabCreateDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/create/labs/LabCreateDTO.java
@@ -18,6 +18,7 @@
 package nl.tudelft.queue.dto.create.labs;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -33,9 +34,12 @@ import nl.tudelft.labracore.api.dto.SessionDetailsDTO;
 import nl.tudelft.queue.dto.create.QueueSessionCreateDTO;
 import nl.tudelft.queue.model.embeddables.AllowedRequest;
 import nl.tudelft.queue.model.enums.CommunicationMethod;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
 import nl.tudelft.queue.model.labs.Lab;
 
+import org.springframework.util.CollectionUtils;
+
 @Data
 @SuperBuilder
 @NoArgsConstructor
@@ -51,6 +55,9 @@ public abstract class LabCreateDTO<D extends Lab> extends QueueSessionCreateDTO<
 	@Builder.Default
 	private Map<Long, Set<RequestType>> requestTypes = new HashMap<>();
 
+	@Builder.Default
+	private Set<OnlineMode> onlineModes = new HashSet<>();
+
 	@Builder.Default
 	private Boolean enableExperimental = false;
 
@@ -65,6 +72,8 @@ public abstract class LabCreateDTO<D extends Lab> extends QueueSessionCreateDTO<
 				.collect(Collectors.groupingBy(AllowedRequest::getAssignment,
 						Collectors.mapping(AllowedRequest::getType, Collectors.toSet())));
 
+		this.onlineModes = new HashSet<>(lab.getOnlineModes());
+
 		this.enableExperimental = lab.getEnableExperimental();
 	}
 
@@ -77,6 +86,12 @@ public abstract class LabCreateDTO<D extends Lab> extends QueueSessionCreateDTO<
 		nonEmpty("requestTypes", requestTypes);
 
 		nonNull("enableExperimental", enableExperimental);
+
+		if (CollectionUtils.isEmpty(rooms) && CollectionUtils.isEmpty(onlineModes)) {
+			errors.rejectValue("rooms", "Select at least 1 room or online mode");
+			errors.rejectValue("onlineModes", "Select at least 1 room or online mode");
+		}
+
 	}
 
 	@Override
diff --git a/src/main/java/nl/tudelft/queue/dto/create/requests/LabRequestCreateDTO.java b/src/main/java/nl/tudelft/queue/dto/create/requests/LabRequestCreateDTO.java
index f09d6ff1c1d6e92e09b66be4ded7cf3d84eafd16..8b535e49adc7ccb51a75fe5bbc6194b54752fb59 100644
--- a/src/main/java/nl/tudelft/queue/dto/create/requests/LabRequestCreateDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/create/requests/LabRequestCreateDTO.java
@@ -35,6 +35,7 @@ import nl.tudelft.queue.dto.create.RequestCreateDTO;
 import nl.tudelft.queue.dto.id.TimeSlotIdDTO;
 import nl.tudelft.queue.model.LabRequest;
 import nl.tudelft.queue.model.embeddables.AllowedRequest;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
 import nl.tudelft.queue.model.labs.Lab;
 import nl.tudelft.queue.model.labs.SlottedLab;
@@ -60,9 +61,10 @@ public class LabRequestCreateDTO extends RequestCreateDTO<LabRequest, Lab> {
 	@NotNull
 	private Long assignment;
 
-	@NotNull
 	private Long room;
 
+	private OnlineMode onlineMode;
+
 	private transient Lab session;
 
 	@Override
@@ -73,8 +75,13 @@ public class LabRequestCreateDTO extends RequestCreateDTO<LabRequest, Lab> {
 			nonEmpty("question", question);
 		}
 
+		if ((room == null) == (onlineMode == null)) {
+			errors.rejectValue("room", "Room or online mode not selected");
+			errors.rejectValue("onlineMode", "Room or online mode not selected");
+		}
+
 		var session = getBean(SessionCacheManager.class).getOrThrow(this.getSession().getSession());
-		if (session.getRooms().stream().noneMatch(r -> Objects.equals(r.getId(), room))) {
+		if (room != null && session.getRooms().stream().noneMatch(r -> Objects.equals(r.getId(), room))) {
 			errors.rejectValue("room", "A room with id " + room + " is not available in the lab.");
 		}
 
diff --git a/src/main/java/nl/tudelft/queue/dto/patch/LabPatchDTO.java b/src/main/java/nl/tudelft/queue/dto/patch/LabPatchDTO.java
index cf70084da0f345957cee494442ae386e700a635b..abcb220d9ea369ebd5dda648b7bc5ec759546db3 100644
--- a/src/main/java/nl/tudelft/queue/dto/patch/LabPatchDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/patch/LabPatchDTO.java
@@ -17,9 +17,7 @@
  */
 package nl.tudelft.queue.dto.patch;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import javax.validation.constraints.Max;
@@ -28,7 +26,9 @@ import javax.validation.constraints.Min;
 import lombok.*;
 import lombok.experimental.SuperBuilder;
 import nl.tudelft.labracore.api.dto.AssignmentIdDTO;
+import nl.tudelft.labracore.api.dto.RoomIdDTO;
 import nl.tudelft.queue.model.enums.CommunicationMethod;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
 import nl.tudelft.queue.model.labs.Lab;
 
@@ -47,12 +47,19 @@ public abstract class LabPatchDTO<D extends Lab> extends QueueSessionPatchDTO<D>
 	@Builder.Default
 	private Map<Long, Set<RequestType>> requestTypes = null;
 
+	@Builder.Default
+	private Set<OnlineMode> onlineModes = null;
+
 	private Boolean enableExperimental;
 
 	@Override
 	protected void applyOneToOne() {
 		updateNonNull(communicationMethod, data::setCommunicationMethod);
 		updateNonNull(eolGracePeriod, data::setEolGracePeriod);
+		if (onlineModes == null && rooms != null) {
+			onlineModes = new HashSet<>();
+		}
+		updateNonNull(onlineModes, data::setOnlineModes);
 		data.setEnableExperimental(Boolean.TRUE.equals(enableExperimental)); // null is false as well
 	}
 
@@ -65,7 +72,18 @@ public abstract class LabPatchDTO<D extends Lab> extends QueueSessionPatchDTO<D>
 
 	@Override
 	protected void validate() {
+		super.validate();
+
 		nonEmpty("requestTypes", requestTypes);
+
+		// A null value does not update labracore or queue, therefore we set it to an empty list.
+		// No verification needed to check
+		if (rooms == null && onlineModes != null) {
+			rooms = new HashSet<>();
+		} else if (onlineModes == null && rooms != null) {
+			onlineModes = new HashSet<>();
+		}
+
 	}
 
 	@Override
@@ -80,6 +98,24 @@ public abstract class LabPatchDTO<D extends Lab> extends QueueSessionPatchDTO<D>
 				.collect(Collectors.toList());
 	}
 
+	/**
+	 * Overriden method, since if it returns null, it usually signifies "no change" in labracore. From now it
+	 * will signify that this is a sesion with online modes only.
+	 *
+	 * @return A list of roomIdDTOs
+	 */
+	@Override
+	public List<RoomIdDTO> roomIdDTOs() {
+		if (rooms == null) {
+			if (onlineModes != null) {
+				return new ArrayList<>();
+			}
+			return null;
+		}
+
+		return rooms.stream().map(al -> new RoomIdDTO().id(al)).collect(Collectors.toList());
+	}
+
 	private void nonEmpty(String field, Map<?, ?> set) {
 		if (set != null && set.isEmpty()) {
 			errors.rejectValue(field, "Field should not be empty");
diff --git a/src/main/java/nl/tudelft/queue/dto/patch/QueueSessionPatchDTO.java b/src/main/java/nl/tudelft/queue/dto/patch/QueueSessionPatchDTO.java
index 631d5945cbec87bf6f7b240b8d7f895082dc5c39..72d862c244b4755ececd69e2a1ce9f85294c781c 100644
--- a/src/main/java/nl/tudelft/queue/dto/patch/QueueSessionPatchDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/patch/QueueSessionPatchDTO.java
@@ -44,7 +44,7 @@ public abstract class QueueSessionPatchDTO<D extends QueueSession<?>> extends Pa
 	private Set<Long> modules = null;
 
 	@Builder.Default
-	private Set<Long> rooms = null;
+	protected Set<Long> rooms = null;
 
 	@Builder.Default
 	private LabRequestConstraintsPatchDTO constraints = new LabRequestConstraintsPatchDTO();
@@ -64,7 +64,6 @@ public abstract class QueueSessionPatchDTO<D extends QueueSession<?>> extends Pa
 	@Override
 	protected void validate() {
 		nonEmpty("modules", modules);
-		nonEmpty("rooms", rooms);
 	}
 
 	/**
@@ -83,7 +82,7 @@ public abstract class QueueSessionPatchDTO<D extends QueueSession<?>> extends Pa
 		return rooms.stream().map(al -> new RoomIdDTO().id(al)).collect(Collectors.toList());
 	}
 
-	private void nonEmpty(String field, Collection<?> set) {
+	protected void nonEmpty(String field, Collection<?> set) {
 		if (set != null && set.isEmpty()) {
 			errors.rejectValue(field, "Field should not be empty");
 		}
diff --git a/src/main/java/nl/tudelft/queue/dto/patch/RequestPatchDTO.java b/src/main/java/nl/tudelft/queue/dto/patch/RequestPatchDTO.java
index 25294984fcfc48b73e22c4faf92e534074aad135..c6f18b3bf902a343f78c1dbfc1c4c47534cc2d54 100644
--- a/src/main/java/nl/tudelft/queue/dto/patch/RequestPatchDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/patch/RequestPatchDTO.java
@@ -48,7 +48,7 @@ public class RequestPatchDTO extends Patch<LabRequest> {
 	protected void validate() {
 		var session = SpringContext.getBean(SessionCacheManager.class)
 				.getOrThrow(data.getSession().getSession());
-		if (session.getRooms().stream().noneMatch(r -> Objects.equals(r.getId(), room))) {
+		if (room != null && session.getRooms().stream().noneMatch(r -> Objects.equals(r.getId(), room))) {
 			errors.rejectValue("room", "Room is not found in the surrounding lab");
 		}
 
diff --git a/src/main/java/nl/tudelft/queue/dto/patch/labs/CapacitySessionPatchDTO.java b/src/main/java/nl/tudelft/queue/dto/patch/labs/CapacitySessionPatchDTO.java
index cae2edb250867e4c5fcedd103abc74fa348f2776..4d8b8ec28c5ed51d6b5d2a5a6a6e097120c54245 100644
--- a/src/main/java/nl/tudelft/queue/dto/patch/labs/CapacitySessionPatchDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/patch/labs/CapacitySessionPatchDTO.java
@@ -40,6 +40,12 @@ public class CapacitySessionPatchDTO extends QueueSessionPatchDTO<CapacitySessio
 	@Builder.Default
 	private CapacitySessionConfigPatchDTO capacitySessionConfig = new CapacitySessionConfigPatchDTO();
 
+	@Override
+	public void validate() {
+		super.validate();
+		nonEmpty("rooms", rooms);
+	}
+
 	@Override
 	protected void postApply() {
 		super.postApply();
diff --git a/src/main/java/nl/tudelft/queue/dto/util/RequestTableFilterDTO.java b/src/main/java/nl/tudelft/queue/dto/util/RequestTableFilterDTO.java
index 86ef532e4ccc81cfb7ba17eec0c75d60af72fb23..77fc3722a3ed8be2424757f9d6da5f46c76d6847 100644
--- a/src/main/java/nl/tudelft/queue/dto/util/RequestTableFilterDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/util/RequestTableFilterDTO.java
@@ -29,6 +29,7 @@ import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
 import nl.tudelft.librador.dto.Validated;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestStatus;
 import nl.tudelft.queue.model.enums.RequestType;
 
@@ -43,6 +44,9 @@ public class RequestTableFilterDTO extends Validated implements Serializable {
 	private Set<Long> assignments = new HashSet<>();
 	@NotNull
 	private Set<Long> rooms = new HashSet<>();
+
+	@NotNull
+	private Set<OnlineMode> onlineModes = new HashSet<>();
 	@NotNull
 	private Set<Long> assigned = new HashSet<>();
 	@NotNull
@@ -60,7 +64,7 @@ public class RequestTableFilterDTO extends Validated implements Serializable {
 	 * @return The number of lists which contain at least 1 element to filter on.
 	 */
 	public long countActiveFilters() {
-		return Stream.of(labs, assigned, rooms, assigned, assignments, requestStatuses,
+		return Stream.of(labs, assigned, rooms, onlineModes, assigned, assignments, requestStatuses,
 				requestTypes).filter(s -> !s.isEmpty()).count();
 	}
 
@@ -70,7 +74,7 @@ public class RequestTableFilterDTO extends Validated implements Serializable {
 	 * @return A stream of Sets.
 	 */
 	private Stream<Set<?>> getAllFiltersAsStream() {
-		return Stream.of(labs, assigned, rooms, assigned, assignments, requestStatuses,
+		return Stream.of(labs, assigned, rooms, onlineModes, assigned, assignments, requestStatuses,
 				requestTypes);
 	}
 
diff --git a/src/main/java/nl/tudelft/queue/dto/view/LabViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/LabViewDTO.java
index e7799ea679224a2de128a21f86d69bf3e439156a..60ea91065092e043bf36c000ae2dcadf5af01d9d 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/LabViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/LabViewDTO.java
@@ -22,6 +22,7 @@ import java.util.Set;
 import lombok.*;
 import nl.tudelft.queue.model.embeddables.AllowedRequest;
 import nl.tudelft.queue.model.enums.CommunicationMethod;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.labs.Lab;
 
 @Data
@@ -34,4 +35,7 @@ public abstract class LabViewDTO<D extends Lab> extends QueueSessionViewDTO<D> {
 	private Set<AllowedRequest> allowedRequests;
 
 	private Boolean enableExperimental;
+
+	private Set<OnlineMode> onlineModes;
+
 }
diff --git a/src/main/java/nl/tudelft/queue/dto/view/events/StudentNotFoundEventViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/events/StudentNotFoundEventViewDTO.java
index cae29aee2f293b9fa4f7164c5c8f3fa44a6123bc..935d4197bf49a005c6ea4b98cb4255be510e046a 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/events/StudentNotFoundEventViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/events/StudentNotFoundEventViewDTO.java
@@ -39,11 +39,12 @@ public class StudentNotFoundEventViewDTO extends RequestEventViewDTO<StudentNotF
 	 */
 	@Override
 	public String getDescription() {
+		if (request.getOnlineMode() != null) {
+			return "Student did not connect on time";
+		}
 		switch (getRequest().getSession().getCommunicationMethod()) {
-			case JITSI_MEET:
-				return "Student did not connect on time";
 			case STUDENT_VISIT_TA:
-				return "Student did not show";
+				return "Student did not show up";
 			case TA_VISIT_STUDENT:
 				return "Could not find student to handle the request";
 			default:
diff --git a/src/main/java/nl/tudelft/queue/dto/view/requests/LabRequestViewDTO.java b/src/main/java/nl/tudelft/queue/dto/view/requests/LabRequestViewDTO.java
index 67072fb3a5e71543b59b758ed1dbdc8f73aaab2b..5474720be909e72747cbbcccd11120c1ea69f93a 100644
--- a/src/main/java/nl/tudelft/queue/dto/view/requests/LabRequestViewDTO.java
+++ b/src/main/java/nl/tudelft/queue/dto/view/requests/LabRequestViewDTO.java
@@ -34,6 +34,7 @@ import nl.tudelft.queue.dto.view.RequestViewDTO;
 import nl.tudelft.queue.model.Feedback;
 import nl.tudelft.queue.model.LabRequest;
 import nl.tudelft.queue.model.TimeSlot;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
 
 import org.apache.commons.lang3.ArrayUtils;
@@ -50,6 +51,7 @@ public class LabRequestViewDTO extends RequestViewDTO<LabRequest> {
 
 	private String jitsiRoom;
 	private TimeSlot timeSlot;
+	private OnlineMode onlineMode;
 
 	private RequestType requestType;
 
@@ -61,6 +63,10 @@ public class LabRequestViewDTO extends RequestViewDTO<LabRequest> {
 	public void postApply() {
 		super.postApply();
 
+		if (onlineMode != null) {
+			setRoom(null);
+		}
+
 		assignment = getBean(AssignmentCacheManager.class).getOrThrow(data.getAssignment());
 		timeSlot = data.getTimeSlot();
 
@@ -93,6 +99,7 @@ public class LabRequestViewDTO extends RequestViewDTO<LabRequest> {
 				"Comment",
 				"Question",
 				"Time Slot",
+				"Online Mode",
 				"Request Type",
 				"Assignment");
 	}
@@ -103,6 +110,7 @@ public class LabRequestViewDTO extends RequestViewDTO<LabRequest> {
 				(comment != null) ? comment : "",
 				(question != null) ? question : "",
 				(timeSlot != null) ? timeSlot.toString() : "",
+				(onlineMode != null) ? onlineMode.getDisplayName() : "",
 				requestType.displayName(),
 				assignment.getName());
 	}
diff --git a/src/main/java/nl/tudelft/queue/model/LabRequest.java b/src/main/java/nl/tudelft/queue/model/LabRequest.java
index d520298e9d53e5cbca493c465305c34306dfea9f..989cb6dd657131c5aba693b7d199553a4bf807c9 100644
--- a/src/main/java/nl/tudelft/queue/model/LabRequest.java
+++ b/src/main/java/nl/tudelft/queue/model/LabRequest.java
@@ -33,6 +33,7 @@ import lombok.*;
 import lombok.experimental.SuperBuilder;
 import nl.tudelft.librador.dto.view.View;
 import nl.tudelft.queue.dto.view.requests.LabRequestViewDTO;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
 import nl.tudelft.queue.model.labs.ExamLab;
 import nl.tudelft.queue.model.labs.Lab;
@@ -72,6 +73,11 @@ public class LabRequest extends Request<Lab> {
 	 */
 	private String jitsiRoom;
 
+	/**
+	 * The OnlineMode this request will take place in (Jitsi, MS teams, etc)
+	 */
+	private OnlineMode onlineMode;
+
 	/**
 	 * The timeslot this request occupies. Timeslots can be used as opposed to direct requests to allow for
 	 * reserving a TA for a specific time ahead of the lab starting.
diff --git a/src/main/java/nl/tudelft/queue/model/enums/CommunicationMethod.java b/src/main/java/nl/tudelft/queue/model/enums/CommunicationMethod.java
index a96e9575c76533d491eae97ad1d71fff16eab5b7..5a808afa535004eb91e37d8e06dc066be7c9af45 100644
--- a/src/main/java/nl/tudelft/queue/model/enums/CommunicationMethod.java
+++ b/src/main/java/nl/tudelft/queue/model/enums/CommunicationMethod.java
@@ -31,17 +31,8 @@ public enum CommunicationMethod {
 	),
 	STUDENT_VISIT_TA(
 		"Student visits TA"
-	),
-	JITSI_MEET(
-		"Jitsi Meeting"
 	);
 
 	private final String displayName;
 
-	/**
-	 * @return Whether the communication method is an online method.
-	 */
-	public boolean isOnline() {
-		return JITSI_MEET == this;
-	}
 }
diff --git a/src/main/java/nl/tudelft/queue/model/enums/OnlineMode.java b/src/main/java/nl/tudelft/queue/model/enums/OnlineMode.java
new file mode 100644
index 0000000000000000000000000000000000000000..318871b2e271d7383d1415170c21267d8ec32595
--- /dev/null
+++ b/src/main/java/nl/tudelft/queue/model/enums/OnlineMode.java
@@ -0,0 +1,48 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2021  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.queue.model.enums;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum OnlineMode {
+
+	JITSI(
+		"Jitsi"
+	);
+
+	private final String displayName;
+
+	/**
+	 * Maps the online mode(s) to the display name of the respective mode(s) and concatenates them. Used
+	 * mainly for displaying the available online modes to end users.
+	 *
+	 * @param  onlineModes A set of online modes
+	 * @return             A concatenated string with all the display names
+	 */
+	public static String displayNames(Set<OnlineMode> onlineModes) {
+		return onlineModes.stream().map(OnlineMode::getDisplayName)
+				.sorted(String::compareToIgnoreCase)
+				.collect(Collectors.joining(", "));
+	}
+}
diff --git a/src/main/java/nl/tudelft/queue/model/labs/Lab.java b/src/main/java/nl/tudelft/queue/model/labs/Lab.java
index 5d88cf2213aa1e7ad40bef1dc2c8ecdbe372c9bf..0ced510627ef11c7247a541816426bf0c1df6f48 100644
--- a/src/main/java/nl/tudelft/queue/model/labs/Lab.java
+++ b/src/main/java/nl/tudelft/queue/model/labs/Lab.java
@@ -41,6 +41,7 @@ import nl.tudelft.queue.model.QueueSession;
 import nl.tudelft.queue.model.Request;
 import nl.tudelft.queue.model.embeddables.AllowedRequest;
 import nl.tudelft.queue.model.enums.CommunicationMethod;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
 
 import org.hibernate.validator.constraints.UniqueElements;
@@ -54,8 +55,7 @@ import org.hibernate.validator.constraints.UniqueElements;
 public abstract class Lab extends QueueSession<LabRequest> {
 
 	/**
-	 * The communication method that will be used for the lab. This could range from physical contact to Jitsi
-	 * meetings.
+	 * The communication method that will be used for the lab. Online meetings disregard this.
 	 */
 	@NotNull
 	@Enumerated(EnumType.STRING)
@@ -80,6 +80,15 @@ public abstract class Lab extends QueueSession<LabRequest> {
 	@ElementCollection
 	private Set<AllowedRequest> allowedRequests = new HashSet<>();
 
+	/**
+	 * The online options available for the lab. Currently only Jitsi
+	 */
+	@NotNull
+	@UniqueElements
+	@Builder.Default
+	@ElementCollection
+	private Set<OnlineMode> onlineModes = new HashSet<>();
+
 	@NotNull
 	@Builder.Default
 	private Boolean enableExperimental = false;
@@ -129,8 +138,9 @@ public abstract class Lab extends QueueSession<LabRequest> {
 
 		return request instanceof LabRequest && super.allowsRequest(request)
 				&& allowsRequest((LabRequest) request)
-				&& request.getRoom() != null && session.getRooms().stream()
-						.anyMatch(room -> Objects.equals(room.getId(), request.getRoom()));
+				&& (request.getRoom() == null) ? ((LabRequest) request).getOnlineMode() != null
+						: session.getRooms().stream()
+								.anyMatch(room -> Objects.equals(room.getId(), request.getRoom()));
 	}
 
 	/**
diff --git a/src/main/java/nl/tudelft/queue/realtime/messages/RequestCreatedMessage.java b/src/main/java/nl/tudelft/queue/realtime/messages/RequestCreatedMessage.java
index c0736c9bf9881553c33a3d161a0725368b889f4b..3ef20d8449cb8ccddd0fe2b5fa8bb20b3e829d8e 100644
--- a/src/main/java/nl/tudelft/queue/realtime/messages/RequestCreatedMessage.java
+++ b/src/main/java/nl/tudelft/queue/realtime/messages/RequestCreatedMessage.java
@@ -24,6 +24,7 @@ import nl.tudelft.librador.dto.view.View;
 import nl.tudelft.queue.cache.ModuleCacheManager;
 import nl.tudelft.queue.dto.view.requests.LabRequestViewDTO;
 import nl.tudelft.queue.model.LabRequest;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestStatus;
 import nl.tudelft.queue.model.enums.RequestType;
 
@@ -50,6 +51,10 @@ public class RequestCreatedMessage extends View<LabRequest> implements Message {
 	private Long buildingId;
 	private String buildingName;
 
+	private OnlineMode onlineMode;
+
+	private String onlineModeDisplayName;
+
 	private Long assignmentId;
 	private String assignmentName;
 	private Long moduleId;
@@ -84,10 +89,15 @@ public class RequestCreatedMessage extends View<LabRequest> implements Message {
 
 		requestedBy = view.requesterEntityName();
 
-		roomId = view.getRoom().getId();
-		roomName = view.getRoom().getName();
-		buildingId = view.getRoom().getBuilding().getId();
-		buildingName = view.getRoom().getBuilding().getName();
+		if (view.getRoom() != null) {
+			roomId = view.getRoom().getId();
+			roomName = view.getRoom().getName();
+			buildingId = view.getRoom().getBuilding().getId();
+			buildingName = view.getRoom().getBuilding().getName();
+		} else if (view.getOnlineMode() != null) {
+			onlineMode = view.getOnlineMode();
+			onlineModeDisplayName = view.getOnlineMode().getDisplayName();
+		}
 
 		assignmentId = view.getAssignment().getId();
 		assignmentName = view.getAssignment().getName();
diff --git a/src/main/java/nl/tudelft/queue/repository/LabRequestRepository.java b/src/main/java/nl/tudelft/queue/repository/LabRequestRepository.java
index 658c90981b4ea0f34eae60521f6ce2acf0853746..f38fe483b59cb2f45718a066dec3546e9e6c12f7 100644
--- a/src/main/java/nl/tudelft/queue/repository/LabRequestRepository.java
+++ b/src/main/java/nl/tudelft/queue/repository/LabRequestRepository.java
@@ -364,6 +364,7 @@ public interface LabRequestRepository
 		e = and(e, filter.getAssigned(), qlr.eventInfo.assignedTo::in);
 		e = and(e, filter.getLabs(), qlr.session.id::in);
 		e = and(e, filter.getRooms(), qlr.room::in);
+		e = and(e, filter.getOnlineModes(), qlr.onlineMode::in);
 		e = and(e, filter.getAssignments(), qlr.assignment::in);
 		e = and(e, filter.getRequestStatuses(), qlr.eventInfo.status::in);
 		e = and(e, filter.getRequestTypes(), qlr.requestType::in);
diff --git a/src/main/java/nl/tudelft/queue/service/EditionService.java b/src/main/java/nl/tudelft/queue/service/EditionService.java
index 3e00f24752d6179158fd4e53116e2a660e85a6f9..0fe80542f26985df1d7315e11d052588b54acbc9 100644
--- a/src/main/java/nl/tudelft/queue/service/EditionService.java
+++ b/src/main/java/nl/tudelft/queue/service/EditionService.java
@@ -23,6 +23,7 @@ import static java.util.stream.Collectors.groupingBy;
 
 import java.io.IOException;
 import java.util.*;
+import java.util.List;
 import java.util.stream.Collectors;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
diff --git a/src/main/java/nl/tudelft/queue/service/LabService.java b/src/main/java/nl/tudelft/queue/service/LabService.java
index a9d5d601d2906c5a4e5157650f353a5c8170b613..6fafcbffb695972577644265b2534db8f695862e 100644
--- a/src/main/java/nl/tudelft/queue/service/LabService.java
+++ b/src/main/java/nl/tudelft/queue/service/LabService.java
@@ -22,10 +22,7 @@ import static nl.tudelft.queue.misc.QueueSessionStatus.*;
 
 import java.io.IOException;
 import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import javax.transaction.Transactional;
@@ -50,6 +47,7 @@ import nl.tudelft.queue.misc.QueueSessionStatus;
 import nl.tudelft.queue.model.QueueSession;
 import nl.tudelft.queue.model.TimeSlot;
 import nl.tudelft.queue.model.embeddables.AllowedRequest;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
 import nl.tudelft.queue.model.enums.SelectionProcedure;
 import nl.tudelft.queue.model.labs.AbstractSlottedLab;
@@ -486,4 +484,15 @@ public class LabService {
 		return allowedAssignments;
 	}
 
+	/**
+	 * Gets the online modes in a lab session.
+	 *
+	 * @param  labSessionId The ID of the lab session
+	 * @return              The online modes associated to the lab.
+	 */
+	public Set<OnlineMode> getOnlineModesInLabSession(long labSessionId) {
+		var labs = lr.findAllBySessions(List.of(labSessionId));
+		return labs.stream().map(Lab::getOnlineModes).flatMap(Set::stream).collect(Collectors.toSet());
+	}
+
 }
diff --git a/src/main/java/nl/tudelft/queue/service/RequestService.java b/src/main/java/nl/tudelft/queue/service/RequestService.java
index f73c23c6f6dffad50ec882b6bfb59c41d185720e..baf0ee3c416dec2a59f0a728b0049da86bfa9753 100644
--- a/src/main/java/nl/tudelft/queue/service/RequestService.java
+++ b/src/main/java/nl/tudelft/queue/service/RequestService.java
@@ -36,6 +36,7 @@ import nl.tudelft.queue.model.ClosableTimeSlot;
 import nl.tudelft.queue.model.LabRequest;
 import nl.tudelft.queue.model.Request;
 import nl.tudelft.queue.model.SelectionRequest;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.SelectionProcedure;
 import nl.tudelft.queue.model.events.*;
 import nl.tudelft.queue.model.labs.AbstractSlottedLab;
@@ -123,7 +124,12 @@ public class RequestService {
 
 		if (request instanceof LabRequest) {
 			var labRequest = (LabRequest) request;
-			if (((LabRequest) request).getSession().getCommunicationMethod().isOnline()) {
+			/*
+			 * TODO in the future, delegate to another service layer component,
+			 *  and let it generate a link for the respective online mode.
+			 * This is for the far future, when more online modes need to be supported.
+			 */
+			if (((LabRequest) request).getOnlineMode() == OnlineMode.JITSI) {
 				labRequest.setJitsiRoom(js.createJitsiRoomName(labRequest));
 			}
 			if (((LabRequest) request).getSession().getEnableExperimental()) {
diff --git a/src/main/resources/migrations.yml b/src/main/resources/migrations.yml
index b229e6f9f62cf2879a04c90d2422a08543c62eb3..f3844f29b6b05f670a3eabadac3717a92d687adb 100644
--- a/src/main/resources/migrations.yml
+++ b/src/main/resources/migrations.yml
@@ -795,3 +795,63 @@ databaseChangeLog:
                   name: deleted_at
                   type: TIMESTAMP
             tableName: request
+  # Hybrid lab schema changes
+  - changeSet:
+      id: 1665319404891-1
+      author: hpage (generated)
+      changes:
+        - createTable:
+            columns:
+              - column:
+                  constraints:
+                    nullable: false
+                  name: lab_id
+                  type: BIGINT
+              - column:
+                  name: online_modes
+                  type: INT
+            tableName: lab_online_modes
+  - changeSet:
+      id: 1665319404891-2
+      author: hpage (generated)
+      changes:
+        - addColumn:
+            columns:
+              - column:
+                  name: online_mode
+                  type: INTEGER
+            tableName: lab_request
+  - changeSet:
+      id: 1665319404891-3
+      author: hpage (generated)
+      changes:
+        - createIndex:
+            columns:
+              - column:
+                  name: lab_id
+            indexName: FK5rmsu88a1ow9m60756pckpo4_INDEX_8
+            tableName: lab_online_modes
+  - changeSet:
+      id: 1665319404891-4
+      author: hpage (generated)
+      changes:
+        - addForeignKeyConstraint:
+            baseColumnNames: lab_id
+            baseTableName: lab_online_modes
+            constraintName: FK5rmsu88a1ow9m60756pckpo4
+            deferrable: false
+            initiallyDeferred: false
+            onDelete: RESTRICT
+            onUpdate: RESTRICT
+            referencedColumnNames: id
+            referencedTableName: lab
+            validate: true
+  - changeSet:
+      id: 1665319404891-M1
+      author: Henry Page
+      changes:
+        - sql:
+            comment: Change communication method Jitsi to TA Visit Student
+            sql: update lab set communication_method = 'TA_VISIT_STUDENT' WHERE communication_method = 'JITSI_MEET';
+
+
diff --git a/src/main/resources/static/js/request_table.js b/src/main/resources/static/js/request_table.js
index df87bf80b77b1b01a714f1cdecdd6aefb14f4a5a..74ff96666accf63edbf90ae253c8e35bb39509cd 100644
--- a/src/main/resources/static/js/request_table.js
+++ b/src/main/resources/static/js/request_table.js
@@ -41,6 +41,7 @@ const inFilter = (() => {
     let selectedLabs;
     let selectedAssignments;
     let selectedRooms;
+    let selectedOnlineModes;
     let selectedStatuses;
     let selectedTypes;
 
@@ -49,17 +50,22 @@ const inFilter = (() => {
         selectedLabs = findSelected($("#lab-select"));
         selectedAssignments = findSelected($("#assignment-select"));
         selectedRooms = findSelected($("#room-select"));
+        selectedOnlineModes = findSelected($("#onlineMode-select"));
         selectedStatuses = findSelected($("#status-select"));
         selectedTypes = findSelected($("#request-type-select"));
     });
 
     // Return a function checking whether an incoming request passes the filters
     return event => {
+        const locationConstraintMet =
+            (event["roomId"] && !event["onlineMode"]) ? selectedRooms.includes(event["roomId"].toString()) :
+            (!event["roomId"] && event["onlineMode"]) ? selectedOnlineModes.includes(event["onlineMode"]) : false;
+
         return selectedLabs.includes(event["labId"].toString()) &&
             selectedAssignments.includes(event["assignmentId"].toString()) &&
-            selectedRooms.includes(event["roomId"].toString()) &&
             selectedStatuses.includes(event["status"]) &&
-            selectedTypes.includes(event["requestType"]);
+            selectedTypes.includes(event["requestType"]) &&
+            locationConstraintMet;
     }
 }).apply()
 
diff --git a/src/main/resources/templates/lab/create/components/lab-general.html b/src/main/resources/templates/lab/create/components/lab-general.html
index 4131e10f3e61dc8927ca2e74d5757d91f8221866..0f78eaccc0ebaea3c411fa76f36efc051e3631e4 100644
--- a/src/main/resources/templates/lab/create/components/lab-general.html
+++ b/src/main/resources/templates/lab/create/components/lab-general.html
@@ -44,7 +44,7 @@
             <div class="col-sm-10">
                 <select class="selectpicker form-control" id="direction-select" required name="communicationMethod"
                         th:classappend="${#fields.hasErrors('communicationMethod')} ? 'is-invalid'"
-                        data-title="Pick a direction" onchange="updateJitsi()">
+                        data-title="Pick a direction">
                     <option th:each="cm : ${T(nl.tudelft.queue.model.enums.CommunicationMethod).values()}"
                             th:value="${cm}" th:text="${cm.displayName}"
                             th:selected="${dto.communicationMethod == cm}"></option>
@@ -124,8 +124,8 @@
                 <select multiple class="selectpicker form-control"
                         th:classappend="${#fields.hasErrors('rooms')} ? 'is-invalid'"
                         id="room-select" th:field="*{rooms}" size="10"
-                        data-live-search="true" required
-                        data-title="Pick at least one room"
+                        data-live-search="true"
+                        data-title="Pick a room"
                         data-actions-box="true">
                     <th:block th:each="room : ${rooms}">
                         <option th:value="${room.id}"
@@ -139,6 +139,25 @@
             </div>
         </div>
 
+        <div id="online-mode-select-div" class="form-group form-row mb-4">
+            <label class="col-sm-2 col-form-label" for="online-mode-select">Online Modes</label>
+            <div class="col-sm-10">
+                <select multiple class="selectpicker form-control"
+                        th:classappend="${#fields.hasErrors('onlineModes')} ? 'is-invalid'"
+                        id="online-mode-select" th:field="*{onlineModes}" size="10"
+                        data-live-search="true"
+                        data-title="Pick online mode(s)"
+                        data-actions-box="true">
+                    <th:block th:each="onlineMode : ${T(nl.tudelft.queue.model.enums.OnlineMode).values()}">
+                        <option th:value="${onlineMode}"
+                                th:selected="${dto.onlineModes.contains(onlineMode)}"
+                                th:text="|${onlineMode.getDisplayName()}|">
+                        </option>
+                    </th:block>
+                </select>
+            </div>
+        </div>
+
         <div class="form-group form-row">
             <label class="col-sm-2 col-form-label" for="module-select">Modules</label>
             <div class="col-sm-10">
@@ -255,28 +274,6 @@
                 $("#room-select").selectpicker("refresh");
             }
 
-            function updateJitsi() {
-                const direction = $("#direction-select").val();
-
-                const buildingSelectDiv = $("#building-select-div");
-                const roomSelectDiv = $("#room-select-div");
-
-                if (direction === "JITSI_MEET") {
-                    buildingSelectDiv.addClass("d-none")
-                    roomSelectDiv.addClass("d-none");
-
-                    $("#room-select > option").each(function () {
-                        $(this).attr("selected", $(this).val() === "94");
-                    });
-
-                    $("#room-select").selectpicker("refresh");
-                } else {
-                    buildingSelectDiv.removeClass("d-none")
-                    roomSelectDiv.removeClass("d-none");
-
-                    updateRooms();
-                }
-            }
             //]]>
         </script>
     </th:block>
@@ -284,3 +281,4 @@
 </body>
 </html>
 
+
diff --git a/src/main/resources/templates/lab/create/slotted.html b/src/main/resources/templates/lab/create/slotted.html
index a32e02a55d1dd7a829854cceea9346bdb4b9379c..27a121a12106187cebb77993c731be1340cecaaf 100644
--- a/src/main/resources/templates/lab/create/slotted.html
+++ b/src/main/resources/templates/lab/create/slotted.html
@@ -46,7 +46,7 @@ layout:decorate="~{lab/create/regular}">
     <section layout:fragment="slot-config">
         <h4>Time slots</h4>
         <hr/>
-
+<!--slotted labs only-->
         <div class="form-group form-row">
             <label for="slot-duration-input" class="col-sm-4 col-form-label">Intervals of x minutes:</label>
             <div class="col-sm-4">
diff --git a/src/main/resources/templates/lab/edit/components/lab-general.html b/src/main/resources/templates/lab/edit/components/lab-general.html
index 8ad1e4824c44d191b5de13634767e8e1479a6bc3..cec71e4beb02da428c98b27e3bb4471e6ae6b03e 100644
--- a/src/main/resources/templates/lab/edit/components/lab-general.html
+++ b/src/main/resources/templates/lab/edit/components/lab-general.html
@@ -46,7 +46,7 @@
             <label for="direction-select" class="col-sm-2 col-form-label">Direction:</label>
             <div class="col-sm-10">
                 <select class="selectpicker form-control" id="direction-select"
-                        name="communicationMethod" onchange="updateJitsi()">
+                        name="communicationMethod">
                     <option th:each="cm : ${T(nl.tudelft.queue.model.enums.CommunicationMethod).values()}"
                             th:value="${cm}" th:text="${cm.displayName}"
                             th:selected="${lab.communicationMethod == cm}"></option>
@@ -104,8 +104,7 @@
         <h4>General</h4>
         <hr/>
 
-        <div id="building-select-div" class="form-group form-row mb-4"
-             th:classappend="${lab.communicationMethod.isOnline()} ? 'd-none'">
+        <div id="building-select-div" class="form-group form-row mb-4">
             <label class="col-sm-2 col-form-label" for="building-select">Buildings</label>
             <div class="col-sm-10">
                 <select multiple class="selectpicker form-control" size="10"
@@ -123,14 +122,13 @@
             </div>
         </div>
 
-        <div id="room-select-div" class="form-group form-row mb-4"
-                th:classappend="${lab.communicationMethod.isOnline()} ? 'd-none'">
+        <div id="room-select-div" class="form-group form-row mb-4">
             <label class="col-sm-2 col-form-label" for="room-select">Rooms</label>
             <div class="col-sm-10">
                 <select multiple class="selectpicker form-control"
                         th:classappend="${#fields.hasErrors('rooms')} ? 'is-invalid'"
                         id="room-select" name="rooms" size="10"
-                        data-live-search="true" required>
+                        data-live-search="true">
                     <th:block th:each="r : ${rooms}">
                         <option th:value="${r.id}"
                                 th:classappend="${#lists.isEmpty(lSession.rooms.?[building.id == __${r.building.id}__])} ? 'd-none'"
@@ -142,6 +140,22 @@
                 </select>
             </div>
         </div>
+        <div id="online-mode-select-div" class="form-group form-row mb-4">
+            <label class="col-sm-2 col-form-label" for="online-mode-select">Online Modes</label>
+            <div class="col-sm-10">
+                <select multiple class="selectpicker form-control"
+                        th:classappend="${#fields.hasErrors('onlineModes')} ? 'is-invalid'"
+                        id="online-mode-select" name="onlineModes" size="10"
+                        data-live-search="true">
+                    <th:block th:each="onlineMode : ${T(nl.tudelft.queue.model.enums.OnlineMode).values()}">
+                        <option th:value="${onlineMode}"
+                                th:selected="${@labService.getOnlineModesInLabSession(lSession.id).contains(onlineMode)}"
+                                th:text="|${onlineMode.getDisplayName()}|">
+                        </option>
+                    </th:block>
+                </select>
+            </div>
+        </div>
 
         <div class="form-group form-row">
             <label class="col-sm-2 col-form-label" for="module-select">Modules</label>
@@ -262,29 +276,6 @@
 
                 $("#room-select").selectpicker("refresh");
             }
-
-            function updateJitsi() {
-                const direction = $("#direction-select").val();
-
-                const buildingSelectDiv = $("#building-select-div");
-                const roomSelectDiv = $("#room-select-div");
-
-                if (direction === "JITSI_MEET") {
-                    buildingSelectDiv.addClass("d-none")
-                    roomSelectDiv.addClass("d-none");
-
-                    $("#room-select > option").each(function () {
-                        $(this).attr("selected", $(this).val() === "94");
-                    });
-
-                    $("#room-select").selectpicker("refresh");
-                } else {
-                    buildingSelectDiv.removeClass("d-none")
-                    roomSelectDiv.removeClass("d-none");
-
-                    updateRooms();
-                }
-            }
             //]]>
         </script>
     </th:block>
diff --git a/src/main/resources/templates/lab/enqueue/lab.html b/src/main/resources/templates/lab/enqueue/lab.html
index a6641c3c9d9fbfb7857dbe686b40ec7a744f386d..5a6ba4a4fa378fcfc836206ca1e8a07b3410d277 100644
--- a/src/main/resources/templates/lab/enqueue/lab.html
+++ b/src/main/resources/templates/lab/enqueue/lab.html
@@ -111,7 +111,7 @@
             </div>
         </div>
 
-        <div class="form-group" id="room-div" th:classappend="${qSession.enableExperimental ? 'd-none' : ''}">
+        <div class="form-group" id="room-div" th:unless="${#lists.isEmpty(rooms)}" th:classappend="${qSession.enableExperimental ? 'd-none' : ''}">
             <label for="input-room" class="col-sm-2 control-label">Room</label>
 
             <div class="col-sm-8">
@@ -126,6 +126,26 @@
                     Room error
                 </div>
             </div>
+        </div>
+
+        <div class="form-group" id="onlineMode-div" th:unless="${#sets.isEmpty(qSession.onlineModes)}" th:classappend="${qSession.enableExperimental ? 'd-none' : ''}">
+            <label for="input-onlineMode" class="col-sm-2 control-label">Online Mode</label>
+
+            <div class="col-sm-8">
+                <select class="selectpicker form-control" id="input-onlineMode" th:field="*{onlineMode}"
+                        data-title="Pick an online mode"
+                        th:classappend="${#fields.hasErrors('onlineMode')} ? 'is-invalid'" required>
+                    <option th:each="onlineMode : ${qSession.onlineModes}"
+                            th:id="|input-onlineMode-${onlineMode.name()}|"
+                            th:value="${onlineMode}"
+                            th:text="|${onlineMode.getDisplayName()}|"></option>
+                </select>
+                <div class="invalid-feedback" th:if="${#fields.hasErrors('onlineMode')}" th:errors="*{onlineMode}">
+                    Online Mode error
+                </div>
+            </div>
+
+
         </div>
 
             <div class="form-group" id="question-div" th:classappend="${qSession.enableExperimental ? 'd-none' : ''}">
@@ -182,6 +202,10 @@
             const typeSelect = $("#input-type");
 
             const questionInput = $("#input-question");
+            const commentInput = $("#input-comment");
+
+            const roomSelect = $("#input-room");
+            const onlineModeSelect = $("#input-onlineMode");
 
             assignmentSelect.on("changed.bs.select", () => {
                 typeSelect.val("");
@@ -220,6 +244,23 @@
                     }
                 }
             })
+
+            roomSelect.on("changed.bs.select", () => {
+                onlineModeSelect.prop("selectedIndex",-1);
+                roomSelect.prop("required",true);
+                onlineModeSelect.prop("required",false);
+                commentInput.prop("disabled",false);
+                onlineModeSelect.selectpicker("refresh");
+            });
+            onlineModeSelect.on("changed.bs.select", () => {
+                roomSelect.prop("selectedIndex",-1);
+                onlineModeSelect.prop("required",true);
+                roomSelect.prop("required",false);
+                commentInput.val('');
+                commentInput.prop("disabled",true);
+                roomSelect.selectpicker("refresh");
+            });
+
             //]]>
         </script>
     </th:block>
diff --git a/src/main/resources/templates/lab/view/components/current-request-edit.html b/src/main/resources/templates/lab/view/components/current-request-edit.html
index abcd8bfeb08dc3f1f3756a5650719c46c6f05c03..042615a3f081be48037a20e6bd32657e287d5c18 100644
--- a/src/main/resources/templates/lab/view/components/current-request-edit.html
+++ b/src/main/resources/templates/lab/view/components/current-request-edit.html
@@ -33,31 +33,40 @@
     <div class="col-12 mt-3" th:if="${current.eventInfo.status.isPending()}">
         <div class="card location-info text-white">
             <th:block>
-                <h3 class="card-header">Update your location and question</h3>
+                <h3 class="card-header" th:if="${current.room != null}">Update your location and question</h3>
+                <h3 class="card-header" th:if="${current.onlineMode != null}">Update your question</h3>
                 <div class="card-body col-12 col-md-12">
-                    <span th:text="|Current room: ${current.room.building.name} - ${current.room.name}|"></span>
                     <form class="form" method="post"
                           th:action="@{/request/{id}/update-request-info/(id=${current.id})}"
                           th:object="${currentDto}">
-                        <div class="form-group">
-                            <select class="custom-select custom-select-md"
-                                    th:field="*{room}">
-                                <option disabled value="">Choose your new room</option>
-                                <option th:each="room : ${rooms}"
-                                        th:value="${room.id}"
-                                        th:selected="${current.room == room.id}"
-                                        th:text="|${room.building.name} - ${room.name}|">
-                                </option>
-                            </select>
+                        <div th:if="${current.room != null}" id="physical-location-info">
+                            <span th:text="|Current location: ${current.room?.building?.name} - ${current.room?.name}|"></span>
+                            <div class="form-group">
+                                <select class="custom-select custom-select-md"
+                                        th:field="*{room}">
+                                    <option disabled value="">Choose your new room</option>
+                                    <option th:each="room : ${rooms}"
+                                            th:value="${room.id}"
+                                            th:selected="${current.room == room.id}"
+                                            th:text="|${room.building.name} - ${room.name}|">
+                                    </option>
+                                </select>
+                            </div>
+
+                            <div class="form-group" id="comment">
+                                <label for="inputComment">Where are you located?</label>
+                                <input type="text" class="form-control" id="inputComment"
+                                       placeholder="Cubicle 1..."
+                                       th:field="*{comment}"/>
+                            </div>
                         </div>
 
-                        <div class="form-group" id="comment">
-                            <label for="inputComment">Where are you located?</label>
-                            <input type="text" class="form-control" id="inputComment"
-                                   placeholder="Cubicle 1..."
-                                   th:field="*{comment}"/>
+                        <div th:if="${!#sets.isEmpty(qSession.onlineModes)}" id="online-mode-info">
+
+
                         </div>
 
+
                         <div th:if="${current.requestType == T(nl.tudelft.queue.model.enums.RequestType).QUESTION}"
                              class="form-group" id="question">
                             <label for="inputQuestion">What is your question?</label>
diff --git a/src/main/resources/templates/lab/view/components/lab-info.html b/src/main/resources/templates/lab/view/components/lab-info.html
index f9f9424d5cc61a4e0c4f9a930271041dc500f1bd..6b91618b6565ffa686a3a39f40617e2c747f0f7a 100644
--- a/src/main/resources/templates/lab/view/components/lab-info.html
+++ b/src/main/resources/templates/lab/view/components/lab-info.html
@@ -40,7 +40,22 @@
                 <dd th:text="|${#temporals.format(qSession.session.start, 'dd MMMM yyyy HH:mm')} - ${#temporals.format(qSession.session.end, 'dd MMMM yyyy HH:mm')}|"></dd>
 
                 <dt>Rooms</dt>
-                <dd th:text="${#strings.listJoin(@roomDTOService.names(rooms), ', ')}"></dd>
+                <dd th:if="${#lists.isEmpty(rooms)}"
+                    class="text-danger">
+                    This lab does not have any rooms
+                </dd>
+                <dd th:unless="${#lists.isEmpty(rooms)}"
+                    th:text="${#strings.listJoin(@roomDTOService.names(rooms), ', ')}">
+                </dd>
+
+                <dt>Online Modes</dt>
+                <dd th:if="${#sets.isEmpty(qSession.onlineModes)}"
+                    class="text-danger">
+                    This lab does not have an online alternative
+                </dd>
+                <dd th:unless="${#sets.isEmpty(qSession.onlineModes)}"
+                    th:text="${T(nl.tudelft.queue.model.enums.OnlineMode).displayNames(qSession.onlineModes)}">
+                </dd>
 
                 <dt>Modules</dt>
                 <dd th:if="${#lists.isEmpty(modules)}"
diff --git a/src/main/resources/templates/request/list/filters.html b/src/main/resources/templates/request/list/filters.html
index c1fb374657da598501ae748bdf019afd8fea2363..6a16cb88320760d9dd3afd8811a00c6c36e57ba3 100644
--- a/src/main/resources/templates/request/list/filters.html
+++ b/src/main/resources/templates/request/list/filters.html
@@ -102,6 +102,18 @@
                     </select>
                 </div>
 
+                <div class="form-group">
+                    <label class="form-control-label" for="onlineMode-select">Online Mode</label>
+                    <select multiple class="form-control selectpicker" id="onlineMode-select" th:field="*{onlineModes}">
+                        <th:block th:each="onlineMode : ${T(nl.tudelft.queue.model.enums.OnlineMode).values()}">
+                            <option th:value="${onlineMode}"
+                                    th:selected="${filter.onlineModes.contains(onlineMode)}"
+                                    th:text="|${onlineMode.displayName}|">
+                            </option>
+                        </th:block>
+                    </select>
+                </div>
+
                 <div class="form-group">
                     <label class="form-control-label" for="assigned-select">Assigned</label>
                     <select multiple class="form-control selectpicker" id="assigned-select"
diff --git a/src/main/resources/templates/request/list/request-table.html b/src/main/resources/templates/request/list/request-table.html
index 03d362f4a254cc7ba2d570031bc124c2112ef430..09b08492383bedfb68db2539c4cd8a4daf1a3246 100644
--- a/src/main/resources/templates/request/list/request-table.html
+++ b/src/main/resources/templates/request/list/request-table.html
@@ -66,10 +66,14 @@
                            th:text="${request.requesterEntityName()}">
                         </a>
                     </td>
-                    <td th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'">
+                    <td th:if="${request.room != null && request.room.building != null}" th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'">
                         <a class="text-white" th:href="@{/request/{id}(id=${request.id})}"
                            th:text="|${request.room.building.name} - ${request.room.name}|"></a>
                     </td>
+                    <td th:if="${request.onlineMode != null}" th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'">
+                        <a class="text-white" th:href="@{/request/{id}(id=${request.id})}"
+                           th:text="|${'@Online'} - ${request.onlineMode.displayName}|"></a>
+                    </td>
                     <td th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'">
                         <a class="text-white" th:href="@{/request/{id}(id=${request.id})}"
                            th:text="|${request.assignment.name} (${request.assignment.module.name})|">
@@ -105,7 +109,13 @@
             <td th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'"><span class="badge badge-pill bg-danger" id="status-{{id}}">NEW</span></td>
             <td th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'"><a th:href="${base + '/{{id}}'}" class="text-white">{{requestTypeDisplayName}}</a></td>
             <td th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'"><a th:href="${base + '/{{id}}'}" class="text-white">{{requestedBy}}</a></td>
-            <td th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'"><a th:href="${base + '/{{id}}'}" class="text-white">{{buildingName}} - {{roomName}}</a></td>
+            {{#if roomName}}
+                {{#if buildingName}}
+                    <td th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'"><a th:href="${base + '/{{id}}'}" class="text-white">{{buildingName}} - {{roomName}}</a></td>
+                {{/if}}
+            {{else if onlineMode}}
+                <td th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'"><a th:href="${base + '/{{id}}'}" class="text-white">@Online - {{onlineModeDisplayName}}</a></td>
+            {{/if}}
             <td th:classappend="${@thymeleafConfig.isTheDay()} ? 'ctrl-alt-right'"><a th:href="${base + '/{{id}}'}" class="text-white">{{assignmentName}} ({{moduleName}})</a><br/>
                 <small>Right now</small>
             </td>
diff --git a/src/main/resources/templates/request/view.html b/src/main/resources/templates/request/view.html
index 17d7c98fb21b121cb31d8bebc4a88a2b0b543a48..e07c580e79b5f7c0eb6cf42d408e84132f1e9851 100644
--- a/src/main/resources/templates/request/view.html
+++ b/src/main/resources/templates/request/view.html
@@ -65,7 +65,7 @@
 
         <script src="/js/map_loader.js"></script>
         <script type="text/javascript" th:inline="javascript">
-            const roomId = /*[[${request.room.id}]]*/ 0;
+            const roomId = /*[[${request.room != null} ? ${request.room.id}]]*/ 0;
             updateRequestInfo(roomId);
         </script>
 
diff --git a/src/main/resources/templates/request/view/components/lab-request-info.html b/src/main/resources/templates/request/view/components/lab-request-info.html
index 4229f7f7f9f9931c5ba63fde544a552438b024b7..fe5d9419c7cb8d28730ce5e75f2bef7cd587eba5 100644
--- a/src/main/resources/templates/request/view/components/lab-request-info.html
+++ b/src/main/resources/templates/request/view/components/lab-request-info.html
@@ -49,8 +49,16 @@
                 <dt>Assignment</dt>
                 <dd th:text="${request.assignment.name}"></dd>
 
-                <dt>Room</dt>
-                <dd th:text="|${request.room.building.name} - ${request.room.name}|"></dd>
+                <th:block th:if="${request.room != null}">
+                    <dt>Room</dt>
+                    <dd th:text="|${request.room.building.name} - ${request.room.name}|"></dd>
+                </th:block>
+
+                <th:block th:if="${request.onlineMode != null}">
+                    <dt>Online Mode</dt>
+                    <dd th:text="|${'@Online'} - ${request.onlineMode.displayName}|"></dd>
+                </th:block>
+
 
                 <dt>Type</dt>
                 <dd th:text="${request.requestType.displayName()}"></dd>
@@ -65,7 +73,8 @@
                     <dd th:text="${request.comment}"></dd>
                 </th:block>
 
-                <th:block th:if="${request.getJitsiRoom() != null && @permissionService.canViewRequestJitsiRoom(request.id)}">
+                <!--TODO This block needs to be changed to support other online modes -->
+                <th:block th:if="${request.getOnlineMode() == T(nl.tudelft.queue.model.enums.OnlineMode).JITSI && request.getJitsiRoom() != null && @permissionService.canViewRequestJitsiRoom(request.id)}">
                     <dt>Link to Jitsi Room</dt>
                     <dd>
                         <a th:href="@{${@jitsiService.getJitsiRoomUrl(request.data)}}" th:target="_blank"
diff --git a/src/test/java/nl/tudelft/queue/controller/LabControllerTest.java b/src/test/java/nl/tudelft/queue/controller/LabControllerTest.java
index c487fc419ec87a5eb069490570523165f54c52cf..cb5c0ded1240502fb92d36802ef78abf72d94ef8 100644
--- a/src/test/java/nl/tudelft/queue/controller/LabControllerTest.java
+++ b/src/test/java/nl/tudelft/queue/controller/LabControllerTest.java
@@ -42,8 +42,10 @@ import nl.tudelft.queue.dto.patch.labs.ExamLabPatchDTO;
 import nl.tudelft.queue.dto.patch.labs.RegularLabPatchDTO;
 import nl.tudelft.queue.dto.patch.labs.SlottedLabPatchDTO;
 import nl.tudelft.queue.model.LabRequest;
+import nl.tudelft.queue.model.QueueSession;
 import nl.tudelft.queue.model.SelectionRequest;
 import nl.tudelft.queue.model.embeddables.*;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestStatus;
 import nl.tudelft.queue.model.enums.RequestType;
 import nl.tudelft.queue.model.events.RequestApprovedEvent;
@@ -51,6 +53,7 @@ import nl.tudelft.queue.model.events.RequestForwardedToAnyEvent;
 import nl.tudelft.queue.model.events.RequestTakenEvent;
 import nl.tudelft.queue.model.labs.*;
 import nl.tudelft.queue.repository.*;
+import nl.tudelft.queue.service.JitsiService;
 import nl.tudelft.queue.service.LabService;
 import nl.tudelft.queue.service.RequestService;
 
@@ -59,6 +62,8 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
 import org.springframework.boot.test.context.SpringBootTest;
@@ -82,7 +87,7 @@ class LabControllerTest {
 	@Autowired
 	private MockMvc mvc;
 
-	@Autowired
+	@SpyBean
 	private QueueSessionRepository qsr;
 
 	@SpyBean
@@ -91,6 +96,9 @@ class LabControllerTest {
 	@SpyBean
 	private RequestService rs;
 
+	@SpyBean
+	private JitsiService js;
+
 	@Autowired
 	private RequestRepository rr;
 
@@ -106,6 +114,9 @@ class LabControllerTest {
 	@Autowired
 	private SessionControllerApi sApi;
 
+	@Captor
+	private ArgumentCaptor<QueueSession<LabRequest>> queueSessionArgumentCaptor;
+
 	private PersonSummaryDTO teacher1;
 	private PersonSummaryDTO student5;
 
@@ -121,6 +132,8 @@ class LabControllerTest {
 
 	private Lab regLab1;
 	private Lab expLab1;
+
+	private Lab regHybridLab1;
 	private SlottedLab slottedLab1;
 	private ExamLab examLabNow;
 	private Lab sharedLab;
@@ -156,6 +169,7 @@ class LabControllerTest {
 		slottedLab1 = slr.getById(db.getOopNowSlottedLab1().getId());
 		examLabNow = elr.getById(db.getOopExamNow().getId());
 		sharedLab = db.getRlOopNowSharedLab();
+		regHybridLab1 = db.getOopNowRegularHybridLab1();
 
 		pastLecture = db.getOopPastLecture();
 		qSession2 = db.getOopLectureRandom();
@@ -331,6 +345,35 @@ class LabControllerTest {
 				.build()), eq(student5.getId()), anyBoolean());
 	}
 
+	@Test
+	@WithUserDetails("student5")
+	void enqueWithOnlineModeSelectedWorks() throws Exception {
+		mvc.perform(post("/lab/" + regHybridLab1.getId() + "/enqueue/lab").with(csrf())
+				.queryParam("requestType", "QUESTION")
+				.queryParam("question", "This is the question I am asking??????")
+				.queryParam("assignment", "" + assignment1.getId())
+				.queryParam("onlineMode", "JITSI"))
+				.andExpect(redirectedUrl("/lab/" + regHybridLab1.getId()));
+
+		verify(js, times(1)).createJitsiRoomName(any());
+
+	}
+
+	@Test
+	@WithUserDetails("student5")
+	void enqueHybridLabWithRoomAndOnlineModeShouldFail() throws Exception {
+		mvc.perform(post("/lab/" + regHybridLab1.getId() + "/enqueue/lab").with(csrf())
+				.queryParam("requestType", "QUESTION")
+				.queryParam("question", "This is the question I am asking??????")
+				.queryParam("assignment", "" + assignment1.getId())
+				.queryParam("room", "" + room1.getId())
+				.queryParam("onlineMode", "JITSI"))
+				.andExpect(status().is4xxClientError());
+
+		verify(js, never()).createJitsiRoomName(any());
+
+	}
+
 	@Test
 	@WithUserDetails("student5")
 	void processingRequestForLabForwardsToRequests() throws Exception {
@@ -608,6 +651,54 @@ class LabControllerTest {
 		verify(ls).updateSession(isA(ExamLabPatchDTO.class), isA(ExamLab.class));
 	}
 
+	@Test
+	@WithUserDetails("admin")
+	void sessionWithOnlineModesOnlySucceeds() throws Exception {
+
+		mvc.perform(regularPatch(post("/lab/" + regLab1.getId() + "/edit/regular").with(csrf()))
+				.queryParam("onlineModes", "JITSI"))
+				.andExpect(status().is3xxRedirection())
+				.andExpect(redirectedUrl("/lab/" + regLab1.getId()));
+
+		verify(ls).updateSession(isA(RegularLabPatchDTO.class), isA(RegularLab.class));
+	}
+
+	@Test
+	@WithUserDetails("admin")
+	void createHybridLabWorksNormal() throws Exception {
+
+		mvc.perform(regularCreate(post("/edition/" + oopNow.getId() + "/lab/create/regular"))
+				.queryParam("onlineModes", "JITSI").with(csrf()))
+				.andExpect(status().is3xxRedirection());
+
+		verify(ls).createSessions(isA(RegularLabCreateDTO.class), any(), eq(LabService.SessionType.REGULAR));
+
+		verify(qsr, atLeastOnce()).save(queueSessionArgumentCaptor.capture());
+		List<QueueSession<LabRequest>> qsList = queueSessionArgumentCaptor.getAllValues();
+		var result = qsList.stream().filter(x -> x instanceof Lab).map(x -> (Lab) x)
+				.filter(x -> x.getOnlineModes().size() != 0).findFirst();
+		assertThat(result.isPresent()).isTrue();
+		assertThat(result.get().getOnlineModes()).containsExactlyInAnyOrder(OnlineMode.JITSI);
+	}
+
+	@Test
+	@WithUserDetails("admin")
+	void createInvalidNoRoomNoOnlineModeLabShouldFail() throws Exception {
+		mvc.perform(invalidRegularCreate(post("/edition/" + oopNow.getId() + "/lab/create/regular")))
+				.andExpect(status().is4xxClientError());
+	}
+
+	@Test
+	@WithUserDetails("admin")
+	void createLabWithOnlyOnlineWorks() throws Exception {
+		mvc.perform(invalidRegularCreate(post("/edition/" + oopNow.getId() + "/lab/create/regular"))
+				.queryParam("onlineModes", "JITSI").with(csrf()))
+				.andExpect(status().is3xxRedirection());
+
+		verify(ls).createSessions(isA(RegularLabCreateDTO.class), any(), eq(LabService.SessionType.REGULAR));
+
+	}
+
 	@Test
 	@WithUserDetails("student150")
 	void redirectToEnrollPageWhenNotEnrolled() throws Exception {
@@ -709,4 +800,14 @@ class LabControllerTest {
 				.queryParam("rooms", "" + room1.getId())
 				.queryParam("eolGracePeriod", "15");
 	}
+
+	private MockHttpServletRequestBuilder invalidRegularCreate(MockHttpServletRequestBuilder req) {
+		return req.queryParam("name", "Super Lab")
+				.queryParam("slot.opensAt", "09-09-2020 12:09")
+				.queryParam("slot.closesAt", "10-09-2020 12:09")
+				.queryParam("communicationMethod", "STUDENT_VISIT_TA")
+				.queryParam("modules", "" + labModule.getId())
+				.queryParam("requestTypes['" + assignment1.getId() + "']", "QUESTION")
+				.queryParam("eolGracePeriod", "15");
+	}
 }
diff --git a/src/test/java/nl/tudelft/queue/model/enums/OnlineModeTest.java b/src/test/java/nl/tudelft/queue/model/enums/OnlineModeTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8af5353103edc12831a9f34519d43a8276f1626
--- /dev/null
+++ b/src/test/java/nl/tudelft/queue/model/enums/OnlineModeTest.java
@@ -0,0 +1,41 @@
+/*
+ * Queue - A Queueing system that can be used to handle labs in higher education
+ * Copyright (C) 2016-2021  Delft University of Technology
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+package nl.tudelft.queue.model.enums;
+
+import static nl.tudelft.queue.model.enums.OnlineMode.*;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+class OnlineModeTest {
+
+	@Test
+	void testDisplayNames() {
+		assertThat(OnlineMode.displayNames(Set.of(JITSI))).isEqualTo("Jitsi");
+	}
+
+	@ParameterizedTest
+	@EnumSource(OnlineMode.class)
+	void displayNameIsCapitalized(OnlineMode mode) {
+		assertThat(mode.getDisplayName().charAt(0)).isUpperCase();
+	}
+}
diff --git a/src/test/java/nl/tudelft/queue/service/LabServiceTest.java b/src/test/java/nl/tudelft/queue/service/LabServiceTest.java
index aca75ef83a729c7b2ceaf0f80512f8f244fcedb1..2efe779fccbe3a1a0d2f9fd97b04eab55113e471 100644
--- a/src/test/java/nl/tudelft/queue/service/LabServiceTest.java
+++ b/src/test/java/nl/tudelft/queue/service/LabServiceTest.java
@@ -49,6 +49,7 @@ import nl.tudelft.queue.model.embeddables.AllowedRequest;
 import nl.tudelft.queue.model.embeddables.Slot;
 import nl.tudelft.queue.model.embeddables.SlottedLabConfig;
 import nl.tudelft.queue.model.enums.CommunicationMethod;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
 import nl.tudelft.queue.model.enums.SelectionProcedure;
 import nl.tudelft.queue.model.labs.CapacitySession;
@@ -124,6 +125,8 @@ class LabServiceTest {
 	private LabCreateDTO<RegularLab> createDTO;
 	private LabPatchDTO<RegularLab> labPatchDTO;
 
+	private LabPatchDTO<RegularLab> addOnlineModesPatchDTO;
+
 	private Model model;
 
 	@BeforeEach
@@ -145,7 +148,7 @@ class LabServiceTest {
 		room1 = db.getRoomPc1();
 
 		lab1 = RegularLab.builder()
-				.communicationMethod(CommunicationMethod.JITSI_MEET)
+				.communicationMethod(CommunicationMethod.STUDENT_VISIT_TA)
 				.session(session1.getId())
 				.allowedRequests(Set.of(new AllowedRequest(assignment1.getId(), RequestType.QUESTION)))
 				.build();
@@ -153,13 +156,13 @@ class LabServiceTest {
 				.slottedLabConfig(SlottedLabConfig.builder()
 						.selectionOpensAt(LocalDateTime.now().minusDays(1L))
 						.build())
-				.communicationMethod(CommunicationMethod.JITSI_MEET)
+				.communicationMethod(CommunicationMethod.TA_VISIT_STUDENT)
 				.session(session2.getId())
 				.allowedRequests(Set.of(new AllowedRequest(assignment1.getId(), RequestType.QUESTION)))
 				.build();
 
 		createDTO = RegularLabCreateDTO.builder()
-				.communicationMethod(CommunicationMethod.JITSI_MEET)
+				.communicationMethod(CommunicationMethod.TA_VISIT_STUDENT)
 				.modules(Set.of(module1.getId()))
 				.name("Lab 1")
 				.eolGracePeriod(15)
@@ -182,6 +185,8 @@ class LabServiceTest {
 						.closesAt(LocalDateTime.now().plusHours(1))
 						.build())
 				.build();
+		addOnlineModesPatchDTO = RegularLabPatchDTO.builder()
+				.onlineModes(Set.of(OnlineMode.JITSI)).build();
 	}
 
 	@Test
@@ -419,6 +424,15 @@ class LabServiceTest {
 		assertThat(lab1.getCommunicationMethod()).isEqualTo(CommunicationMethod.STUDENT_VISIT_TA);
 	}
 
+	@Test
+	void updateLabToOnlyhaveOnlineModesWorks() {
+		when(sApi.patchSession(any(), any())).thenReturn(Mono.empty());
+
+		ls.updateSession(addOnlineModesPatchDTO, lab1);
+
+		assertThat(lab1.getOnlineModes()).containsExactly(OnlineMode.JITSI);
+	}
+
 	@Test
 	void updateLabIncludesAssignmentsIfNonEmpty() {
 		when(sApi.patchSession(any(), any())).thenReturn(Mono.empty());
@@ -456,4 +470,11 @@ class LabServiceTest {
 		ls.deleteSession(lab1);
 		assertThat(lab1.getDeletedAt()).isNotNull();
 	}
+
+	@Test
+	void getOnlineModesInLabSession() {
+		var testLab = db.getOopNowRegularHybridLab1();
+		var result = ls.getOnlineModesInLabSession(testLab.getSession());
+		assertThat(result).containsExactlyInAnyOrder(OnlineMode.JITSI);
+	}
 }
diff --git a/src/test/java/test/TestDatabaseLoader.java b/src/test/java/test/TestDatabaseLoader.java
index c0bb52896f038862c4fa552e43aa1554edfcfe46..c9e53734b81642c2b3408a98f4cc51ac84cbb867 100644
--- a/src/test/java/test/TestDatabaseLoader.java
+++ b/src/test/java/test/TestDatabaseLoader.java
@@ -38,6 +38,7 @@ import nl.tudelft.queue.model.constraints.ClusterConstraint;
 import nl.tudelft.queue.model.constraints.ModuleDivisionConstraint;
 import nl.tudelft.queue.model.embeddables.*;
 import nl.tudelft.queue.model.enums.CommunicationMethod;
+import nl.tudelft.queue.model.enums.OnlineMode;
 import nl.tudelft.queue.model.enums.RequestType;
 import nl.tudelft.queue.model.enums.SelectionProcedure;
 import nl.tudelft.queue.model.events.*;
@@ -269,6 +270,8 @@ public class TestDatabaseLoader {
 	private RegularLab oopNowRegularLab3;
 	private RegularLab oopNowRegularLab4;
 
+	private RegularLab oopNowRegularHybridLab1;
+
 	private RegularLab oop20RegLab;
 	private RegularLab oop20RegLabDeleted;
 
@@ -458,6 +461,22 @@ public class TestDatabaseLoader {
 				.modules(Set.of(oopNowLabsModule.getId()))
 				.requests(new ArrayList<>())
 				.build());
+
+		oopNowRegularHybridLab1 = lr.save(RegularLab.builder()
+				.session(createOopNowSessionForLab(
+						"Regular Hybrid Lab 1",
+						List.of(oopNowAssignments[0]),
+						List.of(roomPc1, roomCz1)))
+				.communicationMethod(CommunicationMethod.TA_VISIT_STUDENT)
+				.constraints(LabRequestConstraints.builder()
+						.build())
+				.allowedRequests(Set.of(
+						AllowedRequest.of(oopNowAssignments[0].getId(), RequestType.QUESTION)))
+				.modules(Set.of(oopNowLabsModule.getId()))
+				.requests(new ArrayList<>())
+				.onlineModes(Set.of(OnlineMode.JITSI))
+				.build());
+
 	}
 
 	public <TS extends TimeSlot, T extends AbstractSlottedLab<TS>> T createSlotsForLab(T lab) {