diff --git a/packages/client/src/components/widgets.vue b/packages/client/src/components/widgets.vue
index 85b8ae0ed..0a9769e19 100644
--- a/packages/client/src/components/widgets.vue
+++ b/packages/client/src/components/widgets.vue
@@ -4,7 +4,7 @@
 		<header>
 			<MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" class="mk-widget-select">
 				<template #label>{{ $ts.selectWidget }}</template>
-				<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ $t(`_widgets.${widget}`) }}</option>
+				<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ i18n.t(`_widgets.${widget}`) }}</option>
 			</MkSelect>
 			<MkButton inline primary class="mk-widget-add" @click="addWidget"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
 			<MkButton inline @click="$emit('exit')">{{ $ts.close }}</MkButton>
@@ -26,79 +26,91 @@
 			</template>
 		</XDraggable>
 	</template>
-	<component :is="`mkw-${widget.name}`" v-for="widget in widgets" v-else :key="widget.id" class="widget" :widget="widget" @updateProps="updateWidget(widget.id, $event)"/>
+	<component :is="`mkw-${widget.name}`" v-for="widget in widgets" v-else :key="widget.id" :ref="el => widgetRefs[widget.id] = el" class="widget" :widget="widget" @updateProps="updateWidget(widget.id, $event)" @contextmenu.stop="onContextmenu(widget, $event)"/>
 </div>
 </template>
 
-<script lang="ts">
-import { defineComponent, defineAsyncComponent, reactive, ref, computed } from 'vue';
+<script lang="ts" setup>
+import { defineAsyncComponent, reactive, ref, computed } from 'vue';
 import { v4 as uuid } from 'uuid';
 import MkSelect from '@/components/form/select.vue';
 import MkButton from '@/components/ui/button.vue';
 import { widgets as widgetDefs } from '@/widgets';
+import * as os from '@/os';
+import { i18n } from '@/i18n';
 
-export default defineComponent({
-	components: {
-		XDraggable: defineAsyncComponent(() => import('vuedraggable')),
-		MkSelect,
-		MkButton,
-	},
+const XDraggable = defineAsyncComponent(() => import('vuedraggable'));
 
-	props: {
-		widgets: {
-			type: Array,
-			required: true,
-		},
-		edit: {
-			type: Boolean,
-			required: true,
-		},
-	},
+type Widget = {
+	name: string;
+	id: string;
+	data: Record<string, any>;
+};
 
-	emits: ['updateWidgets', 'addWidget', 'removeWidget', 'updateWidget', 'exit'],
+const props = defineProps<{
+	widgets: Widget[];
+	edit: boolean;
+}>();
 
-	setup(props, context) {
-		const widgetRefs = reactive({});
-		const configWidget = (id: string) => {
-			widgetRefs[id].configure();
-		};
-		const widgetAdderSelected = ref(null);
-		const addWidget = () => {
-			if (widgetAdderSelected.value == null) return;
+const emit = defineEmits<{
+	(ev: 'updateWidgets', widgets: Widget[]): void;
+	(ev: 'addWidget', widget: Widget): void;
+	(ev: 'removeWidget', widget: Widget): void;
+	(ev: 'updateWidget', widget: Partial<Widget>): void;
+	(ev: 'exit'): void;
+}>();
 
-			context.emit('addWidget', {
-				name: widgetAdderSelected.value,
-				id: uuid(),
-				data: {},
-			});
+const widgetRefs = {};
+const configWidget = (id: string) => {
+	widgetRefs[id].configure();
+};
+const widgetAdderSelected = ref(null);
+const addWidget = () => {
+	if (widgetAdderSelected.value == null) return;
 
-			widgetAdderSelected.value = null;
-		};
-		const removeWidget = (widget) => {
-			context.emit('removeWidget', widget);
-		};
-		const updateWidget = (id, data) => {
-			context.emit('updateWidget', { id, data });
-		};
-		const widgets_ = computed({
-			get: () => props.widgets,
-			set: (value) => {
-				context.emit('updateWidgets', value);
-			},
-		});
+	emit('addWidget', {
+		name: widgetAdderSelected.value,
+		id: uuid(),
+		data: {},
+	});
 
-		return {
-			widgetRefs,
-			configWidget,
-			widgetAdderSelected,
-			widgetDefs,
-			addWidget,
-			removeWidget,
-			updateWidget,
-			widgets_,
-		};
+	widgetAdderSelected.value = null;
+};
+const removeWidget = (widget) => {
+	emit('removeWidget', widget);
+};
+const updateWidget = (id, data) => {
+	emit('updateWidget', { id, data });
+};
+const widgets_ = computed({
+	get: () => props.widgets,
+	set: (value) => {
+		emit('updateWidgets', value);
 	},
 });
+
+function onContextmenu(widget: Widget, ev: MouseEvent) {
+	const isLink = (el: HTMLElement) => {
+		if (el.tagName === 'A') return true;
+		if (el.parentElement) {
+			return isLink(el.parentElement);
+		}
+	};
+	if (isLink(ev.target)) return;
+	if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return;
+	if (window.getSelection()?.toString() !== '') return;
+
+	os.contextMenu([{
+		type: 'label',
+		text: i18n.t(`_widgets.${widget.name}`),
+	}, {
+		icon: 'fas fa-cog',
+		text: i18n.ts.settings,
+		action: () => {
+			configWidget(widget.id);
+		},
+	}], ev);
+}
 </script>
 
 <style lang="scss" scoped>