Skip to content

Add comprehensive login page customization options #7374

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
cd99956
feat: add comprehensive login page customization options
strickvl Jun 12, 2025
bac8b42
Merge branch 'main' into main
strickvl Jun 17, 2025
2553f45
feat: replace individual UI flags with unified --custom-strings flag
strickvl Jun 17, 2025
c2eb61d
docs: simplify migration guide to only cover released flags
strickvl Jun 17, 2025
c0189ed
docs: update Docker customization examples to use --custom-strings
strickvl Jun 17, 2025
cacf3cc
Merge branch 'main' into main
strickvl Jun 21, 2025
2b2819e
docs: remove niche customization sections from FAQ and install guide
strickvl Jun 24, 2025
b275d52
refactor: remove redundant custom-strings validation and global variable
strickvl Jun 24, 2025
af2f599
refactor: simplify loadCustomStrings function signature and error han…
strickvl Jun 24, 2025
e89a9af
test: remove outdated tests for deprecated custom UI flags
strickvl Jun 24, 2025
a8b7fbe
refactor: use addResourceBundle instead of re-initializing i18next
strickvl Jun 24, 2025
27ee714
docs: improve custom-strings flag description
strickvl Jun 24, 2025
d5c0c88
refactor: keep --app-name flag as non-deprecated for {{app}} placeholder
strickvl Jun 24, 2025
fd6ca51
refactor: rename --custom-strings flag to --i18n
strickvl Jun 24, 2025
ca12a25
docs: consolidate i18n documentation in guide.md
strickvl Jun 24, 2025
dceb0a6
refactor: simplify --i18n flag to file-only approach
strickvl Jun 24, 2025
9bf53dc
CI fixes
strickvl Jun 27, 2025
354677c
Merge branch 'main' into main
strickvl Jun 27, 2025
0d4bd15
Address test coverage
strickvl Jun 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ You can change the config file's location using the `--config` flag or

The default location respects `$XDG_CONFIG_HOME`.

### Login page customization

You can customize the login page using the `--custom-strings` flag:

```bash
code-server --custom-strings '{"LOGIN_TITLE": "My Code Server", "WELCOME": "Welcome to my portal"}'
```

Or use a JSON file:
```bash
code-server --custom-strings /path/to/custom-strings.json
```

Legacy individual flags (`--app-name`, `--welcome-text`) are still supported but deprecated.

For detailed customization options and examples, see the [customization guide](./customization.md).

## How do I make my keyboard shortcuts work?

Many shortcuts will not work by default, since they'll be "caught" by the browser.
Expand Down
2 changes: 2 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ code-server.
We also have an in-depth [setup and
configuration](https://coder.com/docs/code-server/latest/guide) guide.

You can also customize the login page appearance - see our [customization guide](./customization.md).

## Questions?

See answers to [frequently asked
Expand Down
170 changes: 170 additions & 0 deletions docs/customization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Login Page Customization

code-server allows you to customize the login page appearance and messages through a unified `--custom-strings` flag or legacy CLI arguments.

## Recommended Approach: Custom Strings

The `--custom-strings` flag provides a scalable way to customize any UI text by leveraging the built-in internationalization system.

### Using JSON File

Create a JSON file with your customizations:

```json
{
"WELCOME": "Welcome to {{app}} Development Portal",
"LOGIN_TITLE": "{{app}} Secure Access",
"LOGIN_BELOW": "Please authenticate to continue",
"PASSWORD_PLACEHOLDER": "Enter your access code",
"SUBMIT": "AUTHENTICATE",
"LOGIN_PASSWORD": "Check the config file at {{configFile}} for the password.",
"LOGIN_USING_ENV_PASSWORD": "Access code provided via environment variable",
"LOGIN_USING_HASHED_PASSWORD": "Access code configured securely",
"LOGIN_RATE_LIMIT": "Too many attempts. Please wait before trying again.",
"MISS_PASSWORD": "Access code is required",
"INCORRECT_PASSWORD": "Invalid access code"
}
```

```bash
code-server --custom-strings /path/to/custom-strings.json
```

### Using Inline JSON

```bash
code-server --custom-strings '{"WELCOME": "Welcome to My Dev Portal", "LOGIN_TITLE": "Development Access", "SUBMIT": "SIGN IN"}'
```

### Configuration File

Add to your `~/.config/code-server/config.yaml`:

```yaml
bind-addr: 127.0.0.1:8080
auth: password
password: your-password
custom-strings: |
{
"WELCOME": "Welcome to {{app}} Development Portal",
"LOGIN_TITLE": "{{app}} Secure Access",
"PASSWORD_PLACEHOLDER": "Enter your access code",
"SUBMIT": "AUTHENTICATE"
}
```

## Available Customization Keys

| Key | Description | Default | Supports {{app}} placeholder |
|-----|-------------|---------|------------------------------|
| `WELCOME` | Welcome message on login page | "Welcome to {{app}}" | ✅ |
| `LOGIN_TITLE` | Main title on login page | "{{app}} login" | ✅ |
| `LOGIN_BELOW` | Text below the login title | "Please log in below." | ❌ |
| `PASSWORD_PLACEHOLDER` | Password field placeholder text | "PASSWORD" | ❌ |
| `SUBMIT` | Login button text | "SUBMIT" | ❌ |
| `LOGIN_PASSWORD` | Message for config file password | "Check the config file at {{configFile}} for the password." | ❌ |
| `LOGIN_USING_ENV_PASSWORD` | Message when using `$PASSWORD` env var | "Password was set from $PASSWORD." | ❌ |
| `LOGIN_USING_HASHED_PASSWORD` | Message when using `$HASHED_PASSWORD` env var | "Password was set from $HASHED_PASSWORD." | ❌ |
| `LOGIN_RATE_LIMIT` | Rate limiting error message | "Login rate limited!" | ❌ |
| `MISS_PASSWORD` | Empty password error message | "Missing password" | ❌ |
| `INCORRECT_PASSWORD` | Wrong password error message | "Incorrect password" | ❌ |

## Docker Examples

### Basic Docker Deployment

```bash
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
-v "$PWD/custom-strings.json:/custom-strings.json" \
codercom/code-server:latest --custom-strings /custom-strings.json
```

### Corporate Branding with Inline JSON

```bash
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
codercom/code-server:latest --custom-strings '{
"WELCOME": "Welcome to ACME Corporation Development Portal",
"LOGIN_TITLE": "ACME Dev Portal Access",
"LOGIN_BELOW": "Enter your corporate credentials",
"PASSWORD_PLACEHOLDER": "Corporate Password",
"SUBMIT": "SIGN IN",
"LOGIN_USING_ENV_PASSWORD": "Password managed by IT department"
}'
```

## Legacy Support (Deprecated)

The following flags are still supported but deprecated. Use `--custom-strings` for new deployments:

```bash
# Deprecated - use --custom-strings instead
code-server \
--app-name "My Development Server" \
--welcome-text "Welcome to the development environment"
```

These legacy flags will show deprecation warnings and may be removed in future versions.

## Migration Guide

### From Legacy Flags to Custom Strings

**Old approach:**
```bash
code-server \
--app-name "Dev Portal" \
--welcome-text "Welcome to development"
```

**New approach:**
```bash
code-server --custom-strings '{
"WELCOME": "Welcome to development"
}'
```

**Note:** The `--app-name` flag controls the `{{app}}` placeholder in templates. You can either:
1. Keep using `--app-name` alongside `--custom-strings`
2. Customize the full text without placeholders in your JSON

## Benefits of Custom Strings

- ✅ **Scalable**: Add any new UI strings without new CLI flags
- ✅ **Flexible**: Supports both files and inline JSON
- ✅ **Future-proof**: Automatically supports new UI strings as they're added
- ✅ **Organized**: All customizations in one place
- ✅ **Version-controlled**: JSON files can be tracked in your repository

## Advanced Usage

### Multi-language Support

Create different JSON files for different languages:

```bash
# English
code-server --custom-strings /config/strings-en.json

# Spanish
code-server --custom-strings /config/strings-es.json --locale es
```

### Dynamic Customization

Generate JSON dynamically in scripts:

```bash
#!/bin/bash
COMPANY_NAME="ACME Corp"
cat > /tmp/strings.json << EOF
{
"WELCOME": "Welcome to ${COMPANY_NAME} Development Portal",
"LOGIN_TITLE": "${COMPANY_NAME} Access Portal"
}
EOF

code-server --custom-strings /tmp/strings.json
```
28 changes: 28 additions & 0 deletions docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,34 @@ docker run -it --name code-server -p 127.0.0.1:8080:8080 \
codercom/code-server:latest
```

### Customizing the login page

You can customize the login page using the `--custom-strings` flag:

```bash
# Example with inline JSON customization
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
codercom/code-server:latest --custom-strings '{
"LOGIN_TITLE": "My Development Environment",
"WELCOME": "Welcome to your coding workspace",
"PASSWORD_PLACEHOLDER": "Enter your secure password",
"SUBMIT": "ACCESS"
}'
```

Or mount a JSON file:

```bash
# Example with JSON file
docker run -it --name code-server -p 127.0.0.1:8080:8080 \
-v "$PWD:/home/coder/project" \
-v "$PWD/custom-strings.json:/config/strings.json" \
codercom/code-server:latest --custom-strings /config/strings.json
```

For detailed customization options, see the [customization guide](./customization.md).

Our official image supports `amd64` and `arm64`. For `arm32` support, you can
use a [community-maintained code-server
alternative](https://hub.docker.com/r/linuxserver/code-server).
Expand Down
23 changes: 23 additions & 0 deletions src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export interface UserProvidedArgs extends UserProvidedCodeArgs {
"app-name"?: string
"welcome-text"?: string
"abs-proxy-base-path"?: string
"custom-strings"?: string
/* Positional arguments. */
_?: string[]
}
Expand Down Expand Up @@ -285,16 +286,22 @@ export const options: Options<Required<UserProvidedArgs>> = {
type: "string",
short: "an",
description: "The name to use in branding. Will be shown in titlebar and welcome message",
deprecated: true,
},
"welcome-text": {
type: "string",
short: "w",
description: "Text to show on login page",
deprecated: true,
},
"abs-proxy-base-path": {
type: "string",
description: "The base path to prefix to all absproxy requests",
},
"custom-strings": {
type: "string",
description: "Path to JSON file or raw JSON string with custom UI strings. Merges with default strings and supports all i18n keys.",
},
}

export const optionDescriptions = (opts: Partial<Options<Required<UserProvidedArgs>>> = options): string[] => {
Expand Down Expand Up @@ -459,6 +466,21 @@ export const parse = (
throw new Error("--cert-key is missing")
}

// Validate custom-strings flag
if (args["custom-strings"]) {
try {
// First try to parse as JSON directly
JSON.parse(args["custom-strings"])
} catch (jsonError) {
// If JSON parsing fails, check if it's a valid file path
if (!args["custom-strings"].startsWith("{") && !args["custom-strings"].startsWith("[")) {
// Assume it's a file path - validation will happen later when the file is read
} else {
throw error(`--custom-strings contains invalid JSON: ${jsonError instanceof Error ? jsonError.message : String(jsonError)}`)
}
}
}

logger.debug(() => [`parsed ${opts?.configFile ? "config" : "command line"}`, field("args", redactArgs(args))])

return args
Expand Down Expand Up @@ -593,6 +615,7 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
args["disable-proxy"] = true
}


const usingEnvHashedPassword = !!process.env.HASHED_PASSWORD
if (process.env.HASHED_PASSWORD) {
args["hashed-password"] = process.env.HASHED_PASSWORD
Expand Down
Loading
Loading