diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue
index b2a0a931f9..c968850444 100644
--- a/packages/frontend/src/pages/role.vue
+++ b/packages/frontend/src/pages/role.vue
@@ -18,11 +18,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 	<MkSpacer v-else-if="tab === 'users'" :contentMax="1200">
 		<div class="_gaps_s">
 			<div v-if="role">{{ role.description }}</div>
-			<MkUserList :pagination="users" :extractor="(item) => item.user"/>
+			<MkUserList v-if="visiable" :pagination="users" :extractor="(item) => item.user"/>
+			<div v-else-if="!visiable" class="_fullinfo">
+				<img :src="infoImageUrl" class="_ghost"/>
+				<div>{{ i18n.ts.nothing }}</div>
+			</div>
 		</div>
 	</MkSpacer>
 	<MkSpacer v-else-if="tab === 'timeline'" :contentMax="700">
-		<MkTimeline ref="timeline" src="role" :role="props.role"/>
+		<MkTimeline v-if="visiable" ref="timeline" src="role" :role="props.role"/>
+		<div v-else-if="!visiable" class="_fullinfo">
+			<img :src="infoImageUrl" class="_ghost"/>
+			<div>{{ i18n.ts.nothing }}</div>
+		</div>
 	</MkSpacer>
 </MkStickyContainer>
 </template>
@@ -35,7 +43,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import { instanceName } from '@/config.js';
-import { serverErrorImageUrl } from '@/instance.js';
+import { serverErrorImageUrl, infoImageUrl } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	role: string;
@@ -47,6 +55,7 @@ const props = withDefaults(defineProps<{
 let tab = $ref(props.initialTab);
 let role = $ref();
 let error = $ref();
+let visiable = $ref(false);
 
 watch(() => props.role, () => {
 	os.api('roles/show', {
@@ -54,6 +63,7 @@ watch(() => props.role, () => {
 	}).then(res => {
 		role = res;
 		document.title = `${role?.name} | ${instanceName}`;
+		visiable = res.isExplorable && res.isPublic;
 	}).catch((err) => {
 		if (err.code === 'NO_SUCH_ROLE') {
 			error = i18n.ts.noRole;